workqueue: Changes for v6.7

Just one commit to improve lockdep annotation for work_on_cpu() to avoid
 spurious warnings. I'll send another pull request for workqueue rust
 binding.
 -----BEGIN PGP SIGNATURE-----
 
 iIQEABYIACwWIQTfIjM1kS57o3GsC/uxYfJx3gVYGQUCZUBJqg4cdGpAa2VybmVs
 Lm9yZwAKCRCxYfJx3gVYGb0pAQCmql98CmUazFhn/8W2zMo+GaU9CuMf/Drchmx+
 VeTAfgEAhn2RQGnhhq/G3jfShlBq/9xSA2N1d8OohLfza5ZmfQs=
 =0gh4
 -----END PGP SIGNATURE-----

Merge tag 'wq-for-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq

Pull workqueue update from Tejun Heo:
 "Just one commit to improve lockdep annotation for work_on_cpu() to
  avoid spurious warnings"

* tag 'wq-for-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq:
  workqueue: Provide one lock class key per work_on_cpu() callsite
This commit is contained in:
Linus Torvalds 2023-10-30 20:45:29 -10:00
commit 866b8870b6
2 changed files with 51 additions and 15 deletions

View File

@ -274,18 +274,16 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; }
* to generate better code. * to generate better code.
*/ */
#ifdef CONFIG_LOCKDEP #ifdef CONFIG_LOCKDEP
#define __INIT_WORK(_work, _func, _onstack) \ #define __INIT_WORK_KEY(_work, _func, _onstack, _key) \
do { \ do { \
static struct lock_class_key __key; \
\
__init_work((_work), _onstack); \ __init_work((_work), _onstack); \
(_work)->data = (atomic_long_t) WORK_DATA_INIT(); \ (_work)->data = (atomic_long_t) WORK_DATA_INIT(); \
lockdep_init_map(&(_work)->lockdep_map, "(work_completion)"#_work, &__key, 0); \ lockdep_init_map(&(_work)->lockdep_map, "(work_completion)"#_work, (_key), 0); \
INIT_LIST_HEAD(&(_work)->entry); \ INIT_LIST_HEAD(&(_work)->entry); \
(_work)->func = (_func); \ (_work)->func = (_func); \
} while (0) } while (0)
#else #else
#define __INIT_WORK(_work, _func, _onstack) \ #define __INIT_WORK_KEY(_work, _func, _onstack, _key) \
do { \ do { \
__init_work((_work), _onstack); \ __init_work((_work), _onstack); \
(_work)->data = (atomic_long_t) WORK_DATA_INIT(); \ (_work)->data = (atomic_long_t) WORK_DATA_INIT(); \
@ -294,12 +292,22 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; }
} while (0) } while (0)
#endif #endif
#define __INIT_WORK(_work, _func, _onstack) \
do { \
static __maybe_unused struct lock_class_key __key; \
\
__INIT_WORK_KEY(_work, _func, _onstack, &__key); \
} while (0)
#define INIT_WORK(_work, _func) \ #define INIT_WORK(_work, _func) \
__INIT_WORK((_work), (_func), 0) __INIT_WORK((_work), (_func), 0)
#define INIT_WORK_ONSTACK(_work, _func) \ #define INIT_WORK_ONSTACK(_work, _func) \
__INIT_WORK((_work), (_func), 1) __INIT_WORK((_work), (_func), 1)
#define INIT_WORK_ONSTACK_KEY(_work, _func, _key) \
__INIT_WORK_KEY((_work), (_func), 1, _key)
#define __INIT_DELAYED_WORK(_work, _func, _tflags) \ #define __INIT_DELAYED_WORK(_work, _func, _tflags) \
do { \ do { \
INIT_WORK(&(_work)->work, (_func)); \ INIT_WORK(&(_work)->work, (_func)); \
@ -693,8 +701,32 @@ static inline long work_on_cpu_safe(int cpu, long (*fn)(void *), void *arg)
return fn(arg); return fn(arg);
} }
#else #else
long work_on_cpu(int cpu, long (*fn)(void *), void *arg); long work_on_cpu_key(int cpu, long (*fn)(void *),
long work_on_cpu_safe(int cpu, long (*fn)(void *), void *arg); void *arg, struct lock_class_key *key);
/*
* A new key is defined for each caller to make sure the work
* associated with the function doesn't share its locking class.
*/
#define work_on_cpu(_cpu, _fn, _arg) \
({ \
static struct lock_class_key __key; \
\
work_on_cpu_key(_cpu, _fn, _arg, &__key); \
})
long work_on_cpu_safe_key(int cpu, long (*fn)(void *),
void *arg, struct lock_class_key *key);
/*
* A new key is defined for each caller to make sure the work
* associated with the function doesn't share its locking class.
*/
#define work_on_cpu_safe(_cpu, _fn, _arg) \
({ \
static struct lock_class_key __key; \
\
work_on_cpu_safe_key(_cpu, _fn, _arg, &__key); \
})
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
#ifdef CONFIG_FREEZER #ifdef CONFIG_FREEZER

View File

@ -5622,50 +5622,54 @@ static void work_for_cpu_fn(struct work_struct *work)
} }
/** /**
* work_on_cpu - run a function in thread context on a particular cpu * work_on_cpu_key - run a function in thread context on a particular cpu
* @cpu: the cpu to run on * @cpu: the cpu to run on
* @fn: the function to run * @fn: the function to run
* @arg: the function arg * @arg: the function arg
* @key: The lock class key for lock debugging purposes
* *
* It is up to the caller to ensure that the cpu doesn't go offline. * It is up to the caller to ensure that the cpu doesn't go offline.
* The caller must not hold any locks which would prevent @fn from completing. * The caller must not hold any locks which would prevent @fn from completing.
* *
* Return: The value @fn returns. * Return: The value @fn returns.
*/ */
long work_on_cpu(int cpu, long (*fn)(void *), void *arg) long work_on_cpu_key(int cpu, long (*fn)(void *),
void *arg, struct lock_class_key *key)
{ {
struct work_for_cpu wfc = { .fn = fn, .arg = arg }; struct work_for_cpu wfc = { .fn = fn, .arg = arg };
INIT_WORK_ONSTACK(&wfc.work, work_for_cpu_fn); INIT_WORK_ONSTACK_KEY(&wfc.work, work_for_cpu_fn, key);
schedule_work_on(cpu, &wfc.work); schedule_work_on(cpu, &wfc.work);
flush_work(&wfc.work); flush_work(&wfc.work);
destroy_work_on_stack(&wfc.work); destroy_work_on_stack(&wfc.work);
return wfc.ret; return wfc.ret;
} }
EXPORT_SYMBOL_GPL(work_on_cpu); EXPORT_SYMBOL_GPL(work_on_cpu_key);
/** /**
* work_on_cpu_safe - run a function in thread context on a particular cpu * work_on_cpu_safe_key - run a function in thread context on a particular cpu
* @cpu: the cpu to run on * @cpu: the cpu to run on
* @fn: the function to run * @fn: the function to run
* @arg: the function argument * @arg: the function argument
* @key: The lock class key for lock debugging purposes
* *
* Disables CPU hotplug and calls work_on_cpu(). The caller must not hold * Disables CPU hotplug and calls work_on_cpu(). The caller must not hold
* any locks which would prevent @fn from completing. * any locks which would prevent @fn from completing.
* *
* Return: The value @fn returns. * Return: The value @fn returns.
*/ */
long work_on_cpu_safe(int cpu, long (*fn)(void *), void *arg) long work_on_cpu_safe_key(int cpu, long (*fn)(void *),
void *arg, struct lock_class_key *key)
{ {
long ret = -ENODEV; long ret = -ENODEV;
cpus_read_lock(); cpus_read_lock();
if (cpu_online(cpu)) if (cpu_online(cpu))
ret = work_on_cpu(cpu, fn, arg); ret = work_on_cpu_key(cpu, fn, arg, key);
cpus_read_unlock(); cpus_read_unlock();
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(work_on_cpu_safe); EXPORT_SYMBOL_GPL(work_on_cpu_safe_key);
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
#ifdef CONFIG_FREEZER #ifdef CONFIG_FREEZER