scx_link_sched() carries each failure out of the locked section through err_msg and ret because scx_error() used to take scx_sched_lock and couldn't be called under it. That restriction is gone, so report each failure at the site it's detected and return directly. The scx_error() here claims the exit on the sched being linked, which has no descendants yet, and the locked propagation walk is deferred, so nothing reacquires scx_sched_lock inline.
Showing the changes to kernel (1 of 1 file). Show all
Unified · Split
| @@ -5975,31 +5975,30 @@ static void refresh_watchdog(void) · 5974 unmodified lines | |||
| 5975 | 5975 | ||
| 5976 | s32 scx_link_sched(struct scx_sched *sch) | 5976 | s32 scx_link_sched(struct scx_sched *sch) |
| 5977 | { | 5977 | { |
| 5978 | const char *err_msg = ""; | ||
| 5979 | s32 ret = 0; | ||
| 5980 | |||
| 5981 | scoped_guard(raw_spinlock_irqsave, &scx_bypass_lock) /* for the parent bypass check */ | 5978 | scoped_guard(raw_spinlock_irqsave, &scx_bypass_lock) /* for the parent bypass check */ |
| 5982 | scoped_guard(raw_spinlock, &scx_sched_lock) { | 5979 | scoped_guard(raw_spinlock, &scx_sched_lock) { |
| 5983 | #ifdef CONFIG_EXT_SUB_SCHED | 5980 | #ifdef CONFIG_EXT_SUB_SCHED |
| 5984 | struct scx_sched *parent = scx_parent(sch); | 5981 | struct scx_sched *parent = scx_parent(sch); |
| 5985 | 5982 | ||
| 5986 | if (parent) { | 5983 | if (parent) { |
| 5984 | s32 ret; | ||
| 5985 | |||
| 5987 | /* | 5986 | /* |
| 5988 | * Bypass state is spread across per-cpu flags and a | 5987 | * Bypass state is spread across per-cpu flags and a |
| 5989 | * depth count, so inheriting it is tricky and has no | 5988 | * depth count, so inheriting it is tricky and has no |
| 5990 | * valid use case. Refuse it. | 5989 | * valid use case. Refuse it. |
| 5991 | */ | 5990 | */ |
| 5992 | if (READ_ONCE(parent->bypass_depth)) { | 5991 | if (READ_ONCE(parent->bypass_depth)) { |
| 5993 | err_msg = "parent bypassing"; | 5992 | scx_error(sch, "parent bypassing (%d)", -EBUSY); |
| 5994 | ret = -EBUSY; | 5993 | return -EBUSY; |
| 5995 | break; | ||
| 5996 | } | 5994 | } |
| 5997 | 5995 | ||
| 5998 | ret = rhashtable_lookup_insert_fast(&scx_sched_hash, | 5996 | ret = rhashtable_lookup_insert_fast(&scx_sched_hash, |
| 5999 | &sch->hash_node, scx_sched_hash_params); | 5997 | &sch->hash_node, scx_sched_hash_params); |
| 6000 | if (ret) { | 5998 | if (ret) { |
| 6001 | err_msg = "failed to insert into scx_sched_hash"; | 5999 | scx_error(sch, "failed to insert into scx_sched_hash (%d)", |
| 6002 | break; | 6000 | ret); |
| 6001 | return ret; | ||
| 6003 | } | 6002 | } |
| 6004 | 6003 | ||
| 6005 | list_add_tail_rcu(&sch->sibling, &parent->children); | 6004 | list_add_tail_rcu(&sch->sibling, &parent->children); |
| @@ -6014,9 +6013,8 @@ s32 scx_link_sched(struct scx_sched *sch) · 8 unmodified lines | |||
| 6014 | rhashtable_remove_fast(&scx_sched_hash, &sch->hash_node, | 6013 | rhashtable_remove_fast(&scx_sched_hash, &sch->hash_node, |
| 6015 | scx_sched_hash_params); | 6014 | scx_sched_hash_params); |
| 6016 | list_del_rcu(&sch->sibling); | 6015 | list_del_rcu(&sch->sibling); |
| 6017 | err_msg = "parent disabled"; | 6016 | scx_error(sch, "parent disabled (%d)", -ENOENT); |
| 6018 | ret = -ENOENT; | 6017 | return -ENOENT; |
| 6019 | break; | ||
| 6020 | } | 6018 | } |
| 6021 | 6019 | ||
| 6022 | sch->linked = true; | 6020 | sch->linked = true; |
| @@ -6026,15 +6024,6 @@ s32 scx_link_sched(struct scx_sched *sch) · 3 unmodified lines | |||
| 6026 | list_add_tail_rcu(&sch->all, &scx_sched_all); | 6024 | list_add_tail_rcu(&sch->all, &scx_sched_all); |
| 6027 | } | 6025 | } |
| 6028 | 6026 | ||
| 6029 | /* | ||
| 6030 | * scx_error() takes scx_sched_lock via scx_claim_exit(), so it must run after | ||
| 6031 | * the guard above is released. | ||
| 6032 | */ | ||
| 6033 | if (ret) { | ||
| 6034 | scx_error(sch, "%s (%d)", err_msg, ret); | ||
| 6035 | return ret; | ||
| 6036 | } | ||
| 6037 | |||
| 6038 | refresh_watchdog(); | 6027 | refresh_watchdog(); |
| 6039 | return 0; | 6028 | return 0; |
| 6040 | } | 6029 | } |