Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

xfs: avoid busy loops in GCD

When GCD has no new work to handle, but read, write or reset commands
are outstanding, it currently busy loops, which is a bit suboptimal,
and can lead to softlockup warnings in case of stuck commands.

Change the code so that the task state is only set to running when work
is performed, which looks a bit tricky due to the design of the
reading/writing/resetting lists that contain both in-flight and finished
commands.

Fixes: 080d01c41d44 ("xfs: implement zoned garbage collection")
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Hans Holmberg <hans.holmberg@wdc.com>
Signed-off-by: Carlos Maiolino <cem@kernel.org>

authored by

Christoph Hellwig and committed by
Carlos Maiolino
a8c861f4 f5caeb36

+46 -35
+46 -35
fs/xfs/xfs_zone_gc.c
··· 491 491 struct xfs_rtgroup *victim_rtg = NULL; 492 492 unsigned int bucket; 493 493 494 - if (xfs_is_shutdown(mp)) 495 - return false; 496 - 497 - if (iter->victim_rtg) 498 - return true; 499 - 500 - /* 501 - * Don't start new work if we are asked to stop or park. 502 - */ 503 - if (kthread_should_stop() || kthread_should_park()) 504 - return false; 505 - 506 - if (!xfs_zoned_need_gc(mp)) 507 - return false; 508 - 509 494 spin_lock(&zi->zi_used_buckets_lock); 510 495 for (bucket = 0; bucket < XFS_ZONE_USED_BUCKETS; bucket++) { 511 496 victim_rtg = xfs_zone_gc_pick_victim_from(mp, bucket); ··· 960 975 } while (next); 961 976 } 962 977 978 + static bool 979 + xfs_zone_gc_should_start_new_work( 980 + struct xfs_zone_gc_data *data) 981 + { 982 + if (xfs_is_shutdown(data->mp)) 983 + return false; 984 + if (!xfs_zone_gc_space_available(data)) 985 + return false; 986 + 987 + if (!data->iter.victim_rtg) { 988 + if (kthread_should_stop() || kthread_should_park()) 989 + return false; 990 + if (!xfs_zoned_need_gc(data->mp)) 991 + return false; 992 + if (!xfs_zone_gc_select_victim(data)) 993 + return false; 994 + } 995 + 996 + return true; 997 + } 998 + 963 999 /* 964 1000 * Handle the work to read and write data for GC and to reset the zones, 965 1001 * including handling all completions. ··· 988 982 * Note that the order of the chunks is preserved so that we don't undo the 989 983 * optimal order established by xfs_zone_gc_query(). 990 984 */ 991 - static bool 985 + static void 992 986 xfs_zone_gc_handle_work( 993 987 struct xfs_zone_gc_data *data) 994 988 { ··· 1002 996 zi->zi_reset_list = NULL; 1003 997 spin_unlock(&zi->zi_reset_list_lock); 1004 998 1005 - if (!xfs_zone_gc_select_victim(data) || 1006 - !xfs_zone_gc_space_available(data)) { 1007 - if (list_empty(&data->reading) && 1008 - list_empty(&data->writing) && 1009 - list_empty(&data->resetting) && 1010 - !reset_list) 1011 - return false; 1012 - } 1013 - 1014 - __set_current_state(TASK_RUNNING); 1015 - try_to_freeze(); 1016 - 1017 - if (reset_list) 999 + if (reset_list) { 1000 + set_current_state(TASK_RUNNING); 1018 1001 xfs_zone_gc_reset_zones(data, reset_list); 1002 + } 1019 1003 1020 1004 list_for_each_entry_safe(chunk, next, &data->resetting, entry) { 1021 1005 if (READ_ONCE(chunk->state) != XFS_GC_BIO_DONE) 1022 1006 break; 1007 + set_current_state(TASK_RUNNING); 1023 1008 xfs_zone_gc_finish_reset(chunk); 1024 1009 } 1025 1010 1026 1011 list_for_each_entry_safe(chunk, next, &data->writing, entry) { 1027 1012 if (READ_ONCE(chunk->state) != XFS_GC_BIO_DONE) 1028 1013 break; 1014 + set_current_state(TASK_RUNNING); 1029 1015 xfs_zone_gc_finish_chunk(chunk); 1030 1016 } 1031 1017 ··· 1025 1027 list_for_each_entry_safe(chunk, next, &data->reading, entry) { 1026 1028 if (READ_ONCE(chunk->state) != XFS_GC_BIO_DONE) 1027 1029 break; 1030 + set_current_state(TASK_RUNNING); 1028 1031 xfs_zone_gc_write_chunk(chunk); 1029 1032 } 1030 1033 blk_finish_plug(&plug); 1031 1034 1032 - blk_start_plug(&plug); 1033 - while (xfs_zone_gc_start_chunk(data)) 1034 - ; 1035 - blk_finish_plug(&plug); 1036 - return true; 1035 + if (xfs_zone_gc_should_start_new_work(data)) { 1036 + set_current_state(TASK_RUNNING); 1037 + blk_start_plug(&plug); 1038 + while (xfs_zone_gc_start_chunk(data)) 1039 + ; 1040 + blk_finish_plug(&plug); 1041 + } 1037 1042 } 1038 1043 1039 1044 /* ··· 1060 1059 for (;;) { 1061 1060 set_current_state(TASK_INTERRUPTIBLE | TASK_FREEZABLE); 1062 1061 xfs_set_zonegc_running(mp); 1063 - if (xfs_zone_gc_handle_work(data)) 1062 + 1063 + xfs_zone_gc_handle_work(data); 1064 + 1065 + /* 1066 + * Only sleep if nothing set the state to running. Else check for 1067 + * work again as someone might have queued up more work and woken 1068 + * us in the meantime. 1069 + */ 1070 + if (get_current_state() == TASK_RUNNING) { 1071 + try_to_freeze(); 1064 1072 continue; 1073 + } 1065 1074 1066 1075 if (list_empty(&data->reading) && 1067 1076 list_empty(&data->writing) &&