8475 lines · 239.9 KB · C
RawDownload
 * * CONTEXT: * Grabs and releases wq_pool_mutex. * * Return: * %true if some freezable workqueues are still busy.  %false if freezing * is complete. */bool freeze_workqueues_busy(void){	bool busy = false;	struct workqueue_struct *wq;	struct pool_workqueue *pwq; 	mutex_lock(&wq_pool_mutex); 	WARN_ON_ONCE(!workqueue_freezing); 	list_for_each_entry(wq, &workqueues, list) {		if (!(wq->flags & WQ_FREEZABLE))			continue;		/*		 * nr_active is monotonically decreasing.  It's safe		 * to peek without lock.		 */		rcu_read_lock();		for_each_pwq(pwq, wq) {			WARN_ON_ONCE(pwq->nr_active < 0);			if (pwq->nr_active) {				busy = true;				rcu_read_unlock();				goto out_unlock;			}		}		rcu_read_unlock();	}out_unlock:	mutex_unlock(&wq_pool_mutex);	return busy;} /** * thaw_workqueues - thaw workqueues * * Thaw workqueues.  Normal queueing is restored and all collected * frozen works are transferred to their respective pool worklists. * * CONTEXT: * Grabs and releases wq_pool_mutex, wq->mutex and pool->lock's. */void thaw_workqueues(void){	struct workqueue_struct *wq; 	mutex_lock(&wq_pool_mutex); 	if (!workqueue_freezing)		goto out_unlock; 	workqueue_freezing = false; 	/* restore max_active and repopulate worklist */	list_for_each_entry(wq, &workqueues, list) {		mutex_lock(&wq->mutex);		wq_adjust_max_active(wq);		mutex_unlock(&wq->mutex);	} out_unlock:	mutex_unlock(&wq_pool_mutex);}#endif /* CONFIG_FREEZER */ static int workqueue_apply_unbound_cpumask(const cpumask_var_t unbound_cpumask){	LIST_HEAD(ctxs);	int ret = 0;	struct workqueue_struct *wq;	struct apply_wqattrs_ctx *ctx, *n; 	lockdep_assert_held(&wq_pool_mutex); 	list_for_each_entry(wq, &workqueues, list) {		if (!(wq->flags & WQ_UNBOUND) || (wq->flags & __WQ_DESTROYING))			continue; 		ctx = apply_wqattrs_prepare(wq, wq->unbound_attrs, unbound_cpumask);		if (IS_ERR(ctx)) {			ret = PTR_ERR(ctx);			break;		} 		list_add_tail(&ctx->list, &ctxs);	} 	list_for_each_entry_safe(ctx, n, &ctxs, list) {		if (!ret)			apply_wqattrs_commit(ctx);		apply_wqattrs_cleanup(ctx);	} 	if (!ret) {		int cpu;		struct worker_pool *pool;		struct worker *worker; 		mutex_lock(&wq_pool_attach_mutex);		cpumask_copy(wq_unbound_cpumask, unbound_cpumask);		/* rescuer needs to respect cpumask changes when it is not attached */		list_for_each_entry(wq, &workqueues, list) {			if (wq->rescuer && !wq->rescuer->pool)				unbind_worker(wq->rescuer);		}		/* DISASSOCIATED worker needs to respect wq_unbound_cpumask */		for_each_possible_cpu(cpu) {			for_each_cpu_worker_pool(pool, cpu) {				if (!(pool->flags & POOL_DISASSOCIATED))					continue;				for_each_pool_worker(worker, pool)					unbind_worker(worker);			}		}		mutex_unlock(&wq_pool_attach_mutex);	}	return ret;} /** * workqueue_unbound_housekeeping_update - Propagate housekeeping cpumask update * @hk: the new housekeeping cpumask * * Update the unbound workqueue cpumask on top of the new housekeeping cpumask such * that the effective unbound affinity is the intersection of the new housekeeping * with the requested affinity set via nohz_full=/isolcpus= or sysfs. * * Return: 0 on success and -errno on failure. */int workqueue_unbound_housekeeping_update(const struct cpumask *hk){	cpumask_var_t cpumask;	int ret = 0; 	if (!zalloc_cpumask_var(&cpumask, GFP_KERNEL))		return -ENOMEM; 	mutex_lock(&wq_pool_mutex); 	/*	 * If the operation fails, it will fall back to	 * wq_requested_unbound_cpumask which is initially set to	 * HK_TYPE_DOMAIN house keeping mask and rewritten	 * by any subsequent write to workqueue/cpumask sysfs file.	 */	if (!cpumask_and(cpumask, wq_requested_unbound_cpumask, hk))		cpumask_copy(cpumask, wq_requested_unbound_cpumask);	if (!cpumask_equal(cpumask, wq_unbound_cpumask))		ret = workqueue_apply_unbound_cpumask(cpumask); 	/* Save the current isolated cpumask & export it via sysfs */	if (!ret)		cpumask_andnot(wq_isolated_cpumask, cpu_possible_mask, hk); 	mutex_unlock(&wq_pool_mutex);	free_cpumask_var(cpumask);	return ret;} static int parse_affn_scope(const char *val){	return sysfs_match_string(wq_affn_names, val);} static int wq_affn_dfl_set(const char *val, const struct kernel_param *kp){	struct workqueue_struct *wq;	int affn, cpu; 	affn = parse_affn_scope(val);	if (affn < 0)		return affn;	if (affn == WQ_AFFN_DFL)		return -EINVAL; 	cpus_read_lock();	mutex_lock(&wq_pool_mutex); 	wq_affn_dfl = affn; 	list_for_each_entry(wq, &workqueues, list) {		for_each_online_cpu(cpu)			unbound_wq_update_pwq(wq, cpu);	} 	mutex_unlock(&wq_pool_mutex);	cpus_read_unlock(); 	return 0;} static int wq_affn_dfl_get(char *buffer, const struct kernel_param *kp){	return scnprintf(buffer, PAGE_SIZE, "%s\n", wq_affn_names[wq_affn_dfl]);} static const struct kernel_param_ops wq_affn_dfl_ops = {	.set	= wq_affn_dfl_set,	.get	= wq_affn_dfl_get,}; module_param_cb(default_affinity_scope, &wq_affn_dfl_ops, NULL, 0644); #ifdef CONFIG_SYSFS/* * Workqueues with WQ_SYSFS flag set is visible to userland via * /sys/bus/workqueue/devices/WQ_NAME.  All visible workqueues have the * following attributes. * *  per_cpu		RO bool	: whether the workqueue is per-cpu or unbound *  max_active		RW int	: maximum number of in-flight work items * * Unbound workqueues have the following extra attributes. * *  nice		RW int	: nice value of the workers *  cpumask		RW mask	: bitmask of allowed CPUs for the workers *  affinity_scope	RW str  : worker CPU affinity scope (cache, numa, none) *  affinity_strict	RW bool : worker CPU affinity is strict */struct wq_device {	struct workqueue_struct		*wq;	struct device			dev;}; static struct workqueue_struct *dev_to_wq(struct device *dev){	struct wq_device *wq_dev = container_of(dev, struct wq_device, dev); 	return wq_dev->wq;} static ssize_t per_cpu_show(struct device *dev, struct device_attribute *attr,			    char *buf){	struct workqueue_struct *wq = dev_to_wq(dev); 	return scnprintf(buf, PAGE_SIZE, "%d\n", (bool)!(wq->flags & WQ_UNBOUND));}static DEVICE_ATTR_RO(per_cpu); static ssize_t max_active_show(struct device *dev,			       struct device_attribute *attr, char *buf){	struct workqueue_struct *wq = dev_to_wq(dev); 	return scnprintf(buf, PAGE_SIZE, "%d\n", wq->saved_max_active);} static ssize_t max_active_store(struct device *dev,				struct device_attribute *attr, const char *buf,				size_t count){	struct workqueue_struct *wq = dev_to_wq(dev);	int val; 	if (sscanf(buf, "%d", &val) != 1 || val <= 0)		return -EINVAL; 	workqueue_set_max_active(wq, val);	return count;}static DEVICE_ATTR_RW(max_active); static struct attribute *wq_sysfs_attrs[] = {	&dev_attr_per_cpu.attr,	&dev_attr_max_active.attr,	NULL,}; static umode_t wq_sysfs_is_visible(struct kobject *kobj, struct attribute *a, int n){	struct device *dev = kobj_to_dev(kobj);	struct workqueue_struct *wq = dev_to_wq(dev); 	/*	 * Adjusting max_active breaks ordering guarantee. Changing it has no	 * effect on BH worker. Limit max_active to RO in such case.	 */	if (wq->flags & (WQ_BH | __WQ_ORDERED))		return 0444;	return a->mode;} static const struct attribute_group wq_sysfs_group = {	.is_visible = wq_sysfs_is_visible,	.attrs = wq_sysfs_attrs,};__ATTRIBUTE_GROUPS(wq_sysfs); static ssize_t wq_nice_show(struct device *dev, struct device_attribute *attr,			    char *buf){	struct workqueue_struct *wq = dev_to_wq(dev);	int written; 	mutex_lock(&wq->mutex);	written = scnprintf(buf, PAGE_SIZE, "%d\n", wq->unbound_attrs->nice);	mutex_unlock(&wq->mutex); 	return written;} /* prepare workqueue_attrs for sysfs store operations */static struct workqueue_attrs *wq_sysfs_prep_attrs(struct workqueue_struct *wq){	struct workqueue_attrs *attrs; 	lockdep_assert_held(&wq_pool_mutex); 	attrs = alloc_workqueue_attrs();	if (!attrs)		return NULL; 	copy_workqueue_attrs(attrs, wq->unbound_attrs);	return attrs;} static ssize_t wq_nice_store(struct device *dev, struct device_attribute *attr,			     const char *buf, size_t count){	struct workqueue_struct *wq = dev_to_wq(dev);	struct workqueue_attrs *attrs;	int ret = -ENOMEM; 	mutex_lock(&wq_pool_mutex); 	attrs = wq_sysfs_prep_attrs(wq);	if (!attrs)		goto out_unlock; 	if (sscanf(buf, "%d", &attrs->nice) == 1 &&	    attrs->nice >= MIN_NICE && attrs->nice <= MAX_NICE)		ret = apply_workqueue_attrs_locked(wq, attrs);	else		ret = -EINVAL; out_unlock:	mutex_unlock(&wq_pool_mutex);	free_workqueue_attrs(attrs);	return ret ?: count;} static ssize_t wq_cpumask_show(struct device *dev,			       struct device_attribute *attr, char *buf){	struct workqueue_struct *wq = dev_to_wq(dev);	int written; 	mutex_lock(&wq->mutex);	written = scnprintf(buf, PAGE_SIZE, "%*pb\n",			    cpumask_pr_args(wq->unbound_attrs->cpumask));	mutex_unlock(&wq->mutex);	return written;} static ssize_t wq_cpumask_store(struct device *dev,				struct device_attribute *attr,				const char *buf, size_t count){	struct workqueue_struct *wq = dev_to_wq(dev);	struct workqueue_attrs *attrs;	int ret = -ENOMEM; 	mutex_lock(&wq_pool_mutex); 	attrs = wq_sysfs_prep_attrs(wq);	if (!attrs)		goto out_unlock; 	ret = cpumask_parse(buf, attrs->cpumask);	if (!ret)		ret = apply_workqueue_attrs_locked(wq, attrs); out_unlock:	mutex_unlock(&wq_pool_mutex);	free_workqueue_attrs(attrs);	return ret ?: count;} static ssize_t wq_affn_scope_show(struct device *dev,				  struct device_attribute *attr, char *buf){	struct workqueue_struct *wq = dev_to_wq(dev);	int written; 	mutex_lock(&wq->mutex);	if (wq->unbound_attrs->affn_scope == WQ_AFFN_DFL)		written = scnprintf(buf, PAGE_SIZE, "%s (%s)\n",				    wq_affn_names[WQ_AFFN_DFL],				    wq_affn_names[wq_affn_dfl]);	else		written = scnprintf(buf, PAGE_SIZE, "%s\n",				    wq_affn_names[wq->unbound_attrs->affn_scope]);	mutex_unlock(&wq->mutex); 	return written;} static ssize_t wq_affn_scope_store(struct device *dev,				   struct device_attribute *attr,				   const char *buf, size_t count){	struct workqueue_struct *wq = dev_to_wq(dev);	struct workqueue_attrs *attrs;	int affn, ret = -ENOMEM; 	affn = parse_affn_scope(buf);	if (affn < 0)		return affn; 	mutex_lock(&wq_pool_mutex);	attrs = wq_sysfs_prep_attrs(wq);	if (attrs) {		attrs->affn_scope = affn;		ret = apply_workqueue_attrs_locked(wq, attrs);	}	mutex_unlock(&wq_pool_mutex);	free_workqueue_attrs(attrs);	return ret ?: count;} static ssize_t wq_affinity_strict_show(struct device *dev,				       struct device_attribute *attr, char *buf){	struct workqueue_struct *wq = dev_to_wq(dev); 	return scnprintf(buf, PAGE_SIZE, "%d\n",			 wq->unbound_attrs->affn_strict);} static ssize_t wq_affinity_strict_store(struct device *dev,					struct device_attribute *attr,					const char *buf, size_t count){	struct workqueue_struct *wq = dev_to_wq(dev);	struct workqueue_attrs *attrs;	int v, ret = -ENOMEM; 	if (sscanf(buf, "%d", &v) != 1)		return -EINVAL; 	mutex_lock(&wq_pool_mutex);	attrs = wq_sysfs_prep_attrs(wq);	if (attrs) {		attrs->affn_strict = (bool)v;		ret = apply_workqueue_attrs_locked(wq, attrs);	}	mutex_unlock(&wq_pool_mutex);	free_workqueue_attrs(attrs);	return ret ?: count;} static struct device_attribute wq_sysfs_unbound_attrs[] = {	__ATTR(nice, 0644, wq_nice_show, wq_nice_store),	__ATTR(cpumask, 0644, wq_cpumask_show, wq_cpumask_store),	__ATTR(affinity_scope, 0644, wq_affn_scope_show, wq_affn_scope_store),	__ATTR(affinity_strict, 0644, wq_affinity_strict_show, wq_affinity_strict_store),	__ATTR_NULL,}; static const struct bus_type wq_subsys = {	.name				= "workqueue",	.dev_groups			= wq_sysfs_groups,}; /** *  workqueue_set_unbound_cpumask - Set the low-level unbound cpumask *  @cpumask: the cpumask to set * *  The low-level workqueues cpumask is a global cpumask that limits *  the affinity of all unbound workqueues.  This function check the @cpumask *  and apply it to all unbound workqueues and updates all pwqs of them. * *  Return:	0	- Success *		-EINVAL	- Invalid @cpumask *		-ENOMEM	- Failed to allocate memory for attrs or pwqs. */static int workqueue_set_unbound_cpumask(cpumask_var_t cpumask){	int ret = -EINVAL; 	/*	 * Not excluding isolated cpus on purpose.	 * If the user wishes to include them, we allow that.	 */	cpumask_and(cpumask, cpumask, cpu_possible_mask);	if (!cpumask_empty(cpumask)) {		ret = 0;		mutex_lock(&wq_pool_mutex);		if (!cpumask_equal(cpumask, wq_unbound_cpumask))			ret = workqueue_apply_unbound_cpumask(cpumask);		if (!ret)

Lines 7001–7500 of 8475. Previous lines