aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2013-03-12 14:30:05 -0400
committerTejun Heo <tj@kernel.org>2013-03-12 14:37:07 -0400
commit226223ab3c4118ddd10688cc2c131135848371ab (patch)
tree050da70ca30d34cbc9e21f628935e020c4106894
parent36b519dfc7b57b8f91940a6e346d9a248e501e0d (diff)
workqueue: implement sysfs interface for workqueues
There are cases where workqueue users want to expose control knobs to userland. e.g. Unbound workqueues with custom attributes are scheduled to be used for writeback workers and depending on configuration it can be useful to allow admins to tinker with the priority or allowed CPUs. This patch implements workqueue_sysfs_register(), which makes the workqueue visible under /sys/bus/workqueue/devices/WQ_NAME. There currently are two attributes common to both per-cpu and unbound pools and extra attributes for unbound pools including nice level and cpumask. If alloc_workqueue*() is called with WQ_SYSFS, workqueue_sysfs_register() is called automatically as part of workqueue creation. This is the preferred method unless the workqueue user wants to apply workqueue_attrs before making the workqueue visible to userland. v2: Disallow exposing ordered workqueues as ordered workqueues can't be tuned in any way. Signed-off-by: Tejun Heo <tj@kernel.org>
-rw-r--r--include/linux/workqueue.h8
-rw-r--r--kernel/workqueue.c288
2 files changed, 296 insertions, 0 deletions
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index 5668ab249af5..7f6d29a417c0 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -293,6 +293,7 @@ enum {
293 WQ_MEM_RECLAIM = 1 << 3, /* may be used for memory reclaim */ 293 WQ_MEM_RECLAIM = 1 << 3, /* may be used for memory reclaim */
294 WQ_HIGHPRI = 1 << 4, /* high priority */ 294 WQ_HIGHPRI = 1 << 4, /* high priority */
295 WQ_CPU_INTENSIVE = 1 << 5, /* cpu instensive workqueue */ 295 WQ_CPU_INTENSIVE = 1 << 5, /* cpu instensive workqueue */
296 WQ_SYSFS = 1 << 6, /* visible in sysfs, see wq_sysfs_register() */
296 297
297 __WQ_DRAINING = 1 << 16, /* internal: workqueue is draining */ 298 __WQ_DRAINING = 1 << 16, /* internal: workqueue is draining */
298 __WQ_ORDERED = 1 << 17, /* internal: workqueue is ordered */ 299 __WQ_ORDERED = 1 << 17, /* internal: workqueue is ordered */
@@ -495,4 +496,11 @@ extern bool freeze_workqueues_busy(void);
495extern void thaw_workqueues(void); 496extern void thaw_workqueues(void);
496#endif /* CONFIG_FREEZER */ 497#endif /* CONFIG_FREEZER */
497 498
499#ifdef CONFIG_SYSFS
500int workqueue_sysfs_register(struct workqueue_struct *wq);
501#else /* CONFIG_SYSFS */
502static inline int workqueue_sysfs_register(struct workqueue_struct *wq)
503{ return 0; }
504#endif /* CONFIG_SYSFS */
505
498#endif 506#endif
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index cecd4ffe2c40..c82feac0a878 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -210,6 +210,8 @@ struct wq_flusher {
210 struct completion done; /* flush completion */ 210 struct completion done; /* flush completion */
211}; 211};
212 212
213struct wq_device;
214
213/* 215/*
214 * The externally visible workqueue abstraction is an array of 216 * The externally visible workqueue abstraction is an array of
215 * per-CPU workqueues: 217 * per-CPU workqueues:
@@ -233,6 +235,10 @@ struct workqueue_struct {
233 235
234 int nr_drainers; /* W: drain in progress */ 236 int nr_drainers; /* W: drain in progress */
235 int saved_max_active; /* W: saved pwq max_active */ 237 int saved_max_active; /* W: saved pwq max_active */
238
239#ifdef CONFIG_SYSFS
240 struct wq_device *wq_dev; /* I: for sysfs interface */
241#endif
236#ifdef CONFIG_LOCKDEP 242#ifdef CONFIG_LOCKDEP
237 struct lockdep_map lockdep_map; 243 struct lockdep_map lockdep_map;
238#endif 244#endif
@@ -442,6 +448,8 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(struct worker_pool [NR_STD_WORKER_POOLS],
442static DEFINE_IDR(worker_pool_idr); 448static DEFINE_IDR(worker_pool_idr);
443 449
444static int worker_thread(void *__worker); 450static int worker_thread(void *__worker);
451static void copy_workqueue_attrs(struct workqueue_attrs *to,
452 const struct workqueue_attrs *from);
445 453
446/* allocate ID and assign it to @pool */ 454/* allocate ID and assign it to @pool */
447static int worker_pool_assign_id(struct worker_pool *pool) 455static int worker_pool_assign_id(struct worker_pool *pool)
@@ -3153,6 +3161,281 @@ int keventd_up(void)
3153 return system_wq != NULL; 3161 return system_wq != NULL;
3154} 3162}
3155 3163
3164#ifdef CONFIG_SYSFS
3165/*
3166 * Workqueues with WQ_SYSFS flag set is visible to userland via
3167 * /sys/bus/workqueue/devices/WQ_NAME. All visible workqueues have the
3168 * following attributes.
3169 *
3170 * per_cpu RO bool : whether the workqueue is per-cpu or unbound
3171 * max_active RW int : maximum number of in-flight work items
3172 *
3173 * Unbound workqueues have the following extra attributes.
3174 *
3175 * id RO int : the associated pool ID
3176 * nice RW int : nice value of the workers
3177 * cpumask RW mask : bitmask of allowed CPUs for the workers
3178 */
3179struct wq_device {
3180 struct workqueue_struct *wq;
3181 struct device dev;
3182};
3183
3184static struct workqueue_struct *dev_to_wq(struct device *dev)
3185{
3186 struct wq_device *wq_dev = container_of(dev, struct wq_device, dev);
3187
3188 return wq_dev->wq;
3189}
3190
3191static ssize_t wq_per_cpu_show(struct device *dev,
3192 struct device_attribute *attr, char *buf)
3193{
3194 struct workqueue_struct *wq = dev_to_wq(dev);
3195
3196 return scnprintf(buf, PAGE_SIZE, "%d\n", (bool)!(wq->flags & WQ_UNBOUND));
3197}
3198
3199static ssize_t wq_max_active_show(struct device *dev,
3200 struct device_attribute *attr, char *buf)
3201{
3202 struct workqueue_struct *wq = dev_to_wq(dev);
3203
3204 return scnprintf(buf, PAGE_SIZE, "%d\n", wq->saved_max_active);
3205}
3206
3207static ssize_t wq_max_active_store(struct device *dev,
3208 struct device_attribute *attr,
3209 const char *buf, size_t count)
3210{
3211 struct workqueue_struct *wq = dev_to_wq(dev);
3212 int val;
3213
3214 if (sscanf(buf, "%d", &val) != 1 || val <= 0)
3215 return -EINVAL;
3216
3217 workqueue_set_max_active(wq, val);
3218 return count;
3219}
3220
3221static struct device_attribute wq_sysfs_attrs[] = {
3222 __ATTR(per_cpu, 0444, wq_per_cpu_show, NULL),
3223 __ATTR(max_active, 0644, wq_max_active_show, wq_max_active_store),
3224 __ATTR_NULL,
3225};
3226
3227static ssize_t wq_pool_id_show(struct device *dev,
3228 struct device_attribute *attr, char *buf)
3229{
3230 struct workqueue_struct *wq = dev_to_wq(dev);
3231 struct worker_pool *pool;
3232 int written;
3233
3234 rcu_read_lock_sched();
3235 pool = first_pwq(wq)->pool;
3236 written = scnprintf(buf, PAGE_SIZE, "%d\n", pool->id);
3237 rcu_read_unlock_sched();
3238
3239 return written;
3240}
3241
3242static ssize_t wq_nice_show(struct device *dev, struct device_attribute *attr,
3243 char *buf)
3244{
3245 struct workqueue_struct *wq = dev_to_wq(dev);
3246 int written;
3247
3248 rcu_read_lock_sched();
3249 written = scnprintf(buf, PAGE_SIZE, "%d\n",
3250 first_pwq(wq)->pool->attrs->nice);
3251 rcu_read_unlock_sched();
3252
3253 return written;
3254}
3255
3256/* prepare workqueue_attrs for sysfs store operations */
3257static struct workqueue_attrs *wq_sysfs_prep_attrs(struct workqueue_struct *wq)
3258{
3259 struct workqueue_attrs *attrs;
3260
3261 attrs = alloc_workqueue_attrs(GFP_KERNEL);
3262 if (!attrs)
3263 return NULL;
3264
3265 rcu_read_lock_sched();
3266 copy_workqueue_attrs(attrs, first_pwq(wq)->pool->attrs);
3267 rcu_read_unlock_sched();
3268 return attrs;
3269}
3270
3271static ssize_t wq_nice_store(struct device *dev, struct device_attribute *attr,
3272 const char *buf, size_t count)
3273{
3274 struct workqueue_struct *wq = dev_to_wq(dev);
3275 struct workqueue_attrs *attrs;
3276 int ret;
3277
3278 attrs = wq_sysfs_prep_attrs(wq);
3279 if (!attrs)
3280 return -ENOMEM;
3281
3282 if (sscanf(buf, "%d", &attrs->nice) == 1 &&
3283 attrs->nice >= -20 && attrs->nice <= 19)
3284 ret = apply_workqueue_attrs(wq, attrs);
3285 else
3286 ret = -EINVAL;
3287
3288 free_workqueue_attrs(attrs);
3289 return ret ?: count;
3290}
3291
3292static ssize_t wq_cpumask_show(struct device *dev,
3293 struct device_attribute *attr, char *buf)
3294{
3295 struct workqueue_struct *wq = dev_to_wq(dev);
3296 int written;
3297
3298 rcu_read_lock_sched();
3299 written = cpumask_scnprintf(buf, PAGE_SIZE,
3300 first_pwq(wq)->pool->attrs->cpumask);
3301 rcu_read_unlock_sched();
3302
3303 written += scnprintf(buf + written, PAGE_SIZE - written, "\n");
3304 return written;
3305}
3306
3307static ssize_t wq_cpumask_store(struct device *dev,
3308 struct device_attribute *attr,
3309 const char *buf, size_t count)
3310{
3311 struct workqueue_struct *wq = dev_to_wq(dev);
3312 struct workqueue_attrs *attrs;
3313 int ret;
3314
3315 attrs = wq_sysfs_prep_attrs(wq);
3316 if (!attrs)
3317 return -ENOMEM;
3318
3319 ret = cpumask_parse(buf, attrs->cpumask);
3320 if (!ret)
3321 ret = apply_workqueue_attrs(wq, attrs);
3322
3323 free_workqueue_attrs(attrs);
3324 return ret ?: count;
3325}
3326
3327static struct device_attribute wq_sysfs_unbound_attrs[] = {
3328 __ATTR(pool_id, 0444, wq_pool_id_show, NULL),
3329 __ATTR(nice, 0644, wq_nice_show, wq_nice_store),
3330 __ATTR(cpumask, 0644, wq_cpumask_show, wq_cpumask_store),
3331 __ATTR_NULL,
3332};
3333
3334static struct bus_type wq_subsys = {
3335 .name = "workqueue",
3336 .dev_attrs = wq_sysfs_attrs,
3337};
3338
3339static int __init wq_sysfs_init(void)
3340{
3341 return subsys_virtual_register(&wq_subsys, NULL);
3342}
3343core_initcall(wq_sysfs_init);
3344
3345static void wq_device_release(struct device *dev)
3346{
3347 struct wq_device *wq_dev = container_of(dev, struct wq_device, dev);
3348
3349 kfree(wq_dev);
3350}
3351
3352/**
3353 * workqueue_sysfs_register - make a workqueue visible in sysfs
3354 * @wq: the workqueue to register
3355 *
3356 * Expose @wq in sysfs under /sys/bus/workqueue/devices.
3357 * alloc_workqueue*() automatically calls this function if WQ_SYSFS is set
3358 * which is the preferred method.
3359 *
3360 * Workqueue user should use this function directly iff it wants to apply
3361 * workqueue_attrs before making the workqueue visible in sysfs; otherwise,
3362 * apply_workqueue_attrs() may race against userland updating the
3363 * attributes.
3364 *
3365 * Returns 0 on success, -errno on failure.
3366 */
3367int workqueue_sysfs_register(struct workqueue_struct *wq)
3368{
3369 struct wq_device *wq_dev;
3370 int ret;
3371
3372 /*
3373 * Adjusting max_active or creating new pwqs by applyting
3374 * attributes breaks ordering guarantee. Disallow exposing ordered
3375 * workqueues.
3376 */
3377 if (WARN_ON(wq->flags & __WQ_ORDERED))
3378 return -EINVAL;
3379
3380 wq->wq_dev = wq_dev = kzalloc(sizeof(*wq_dev), GFP_KERNEL);
3381 if (!wq_dev)
3382 return -ENOMEM;
3383
3384 wq_dev->wq = wq;
3385 wq_dev->dev.bus = &wq_subsys;
3386 wq_dev->dev.init_name = wq->name;
3387 wq_dev->dev.release = wq_device_release;
3388
3389 /*
3390 * unbound_attrs are created separately. Suppress uevent until
3391 * everything is ready.
3392 */
3393 dev_set_uevent_suppress(&wq_dev->dev, true);
3394
3395 ret = device_register(&wq_dev->dev);
3396 if (ret) {
3397 kfree(wq_dev);
3398 wq->wq_dev = NULL;
3399 return ret;
3400 }
3401
3402 if (wq->flags & WQ_UNBOUND) {
3403 struct device_attribute *attr;
3404
3405 for (attr = wq_sysfs_unbound_attrs; attr->attr.name; attr++) {
3406 ret = device_create_file(&wq_dev->dev, attr);
3407 if (ret) {
3408 device_unregister(&wq_dev->dev);
3409 wq->wq_dev = NULL;
3410 return ret;
3411 }
3412 }
3413 }
3414
3415 kobject_uevent(&wq_dev->dev.kobj, KOBJ_ADD);
3416 return 0;
3417}
3418
3419/**
3420 * workqueue_sysfs_unregister - undo workqueue_sysfs_register()
3421 * @wq: the workqueue to unregister
3422 *
3423 * If @wq is registered to sysfs by workqueue_sysfs_register(), unregister.
3424 */
3425static void workqueue_sysfs_unregister(struct workqueue_struct *wq)
3426{
3427 struct wq_device *wq_dev = wq->wq_dev;
3428
3429 if (!wq->wq_dev)
3430 return;
3431
3432 wq->wq_dev = NULL;
3433 device_unregister(&wq_dev->dev);
3434}
3435#else /* CONFIG_SYSFS */
3436static void workqueue_sysfs_unregister(struct workqueue_struct *wq) { }
3437#endif /* CONFIG_SYSFS */
3438
3156/** 3439/**
3157 * free_workqueue_attrs - free a workqueue_attrs 3440 * free_workqueue_attrs - free a workqueue_attrs
3158 * @attrs: workqueue_attrs to free 3441 * @attrs: workqueue_attrs to free
@@ -3625,6 +3908,9 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt,
3625 wake_up_process(rescuer->task); 3908 wake_up_process(rescuer->task);
3626 } 3909 }
3627 3910
3911 if ((wq->flags & WQ_SYSFS) && workqueue_sysfs_register(wq))
3912 goto err_destroy;
3913
3628 /* 3914 /*
3629 * workqueue_lock protects global freeze state and workqueues 3915 * workqueue_lock protects global freeze state and workqueues
3630 * list. Grab it, set max_active accordingly and add the new 3916 * list. Grab it, set max_active accordingly and add the new
@@ -3693,6 +3979,8 @@ void destroy_workqueue(struct workqueue_struct *wq)
3693 3979
3694 spin_unlock_irq(&workqueue_lock); 3980 spin_unlock_irq(&workqueue_lock);
3695 3981
3982 workqueue_sysfs_unregister(wq);
3983
3696 if (wq->rescuer) { 3984 if (wq->rescuer) {
3697 kthread_stop(wq->rescuer->task); 3985 kthread_stop(wq->rescuer->task);
3698 kfree(wq->rescuer); 3986 kfree(wq->rescuer);