Current modification to attrs via sysfs is not fully synchronized. Process A (change cpumask) | Process B (change numa affinity) wq_cpumask_store() | wq_sysfs_prep_attrs() | | apply_workqueue_attrs() apply_workqueue_attrs() | It results that the Process B's operation is totally reverted without any notification, it is a buggy behavior. So this patch moves wq_sysfs_prep_attrs() into the protection under wq_pool_mutex to ensure attrs changes are properly synchronized.
Showing the changes to kernel/workqueue.c (1 of 1 file). Show all
Unified · Split
| @@ -4954,18 +4954,22 @@ static ssize_t wq_nice_store(struct device *dev, struct device_attribute *attr, · 4953 unmodified lines | ||
| 4954 | 4954 | { |
| 4955 | 4955 | struct workqueue_struct *wq = dev_to_wq(dev); |
| 4956 | 4956 | struct workqueue_attrs *attrs; |
| 4957 | − int ret; | |
| 4957 | + int ret = -ENOMEM; | |
| 4958 | + | |
| 4959 | + apply_wqattrs_lock(); | |
| 4958 | 4960 | |
| 4959 | 4961 | attrs = wq_sysfs_prep_attrs(wq); |
| 4960 | 4962 | if (!attrs) |
| 4961 | − return -ENOMEM; | |
| 4963 | + goto out_unlock; | |
| 4962 | 4964 | |
| 4963 | 4965 | if (sscanf(buf, "%d", &attrs->nice) == 1 && |
| 4964 | 4966 | attrs->nice >= MIN_NICE && attrs->nice <= MAX_NICE) |
| 4965 | − ret = apply_workqueue_attrs(wq, attrs); | |
| 4967 | + ret = apply_workqueue_attrs_locked(wq, attrs); | |
| 4966 | 4968 | else |
| 4967 | 4969 | ret = -EINVAL; |
| 4968 | 4970 | |
| 4971 | +out_unlock: | |
| 4972 | + apply_wqattrs_unlock(); | |
| 4969 | 4973 | free_workqueue_attrs(attrs); |
| 4970 | 4974 | return ret ?: count; |
| 4971 | 4975 | } |
| @@ -4989,16 +4993,20 @@ static ssize_t wq_cpumask_store(struct device *dev, · 17 unmodified lines | ||
| 4989 | 4993 | { |
| 4990 | 4994 | struct workqueue_struct *wq = dev_to_wq(dev); |
| 4991 | 4995 | struct workqueue_attrs *attrs; |
| 4992 | − int ret; | |
| 4996 | + int ret = -ENOMEM; | |
| 4997 | + | |
| 4998 | + apply_wqattrs_lock(); | |
| 4993 | 4999 | |
| 4994 | 5000 | attrs = wq_sysfs_prep_attrs(wq); |
| 4995 | 5001 | if (!attrs) |
| 4996 | − return -ENOMEM; | |
| 5002 | + goto out_unlock; | |
| 4997 | 5003 | |
| 4998 | 5004 | ret = cpumask_parse(buf, attrs->cpumask); |
| 4999 | 5005 | if (!ret) |
| 5000 | − ret = apply_workqueue_attrs(wq, attrs); | |
| 5006 | + ret = apply_workqueue_attrs_locked(wq, attrs); | |
| 5001 | 5007 | |
| 5008 | +out_unlock: | |
| 5009 | + apply_wqattrs_unlock(); | |
| 5002 | 5010 | free_workqueue_attrs(attrs); |
| 5003 | 5011 | return ret ?: count; |
| 5004 | 5012 | } |
| @@ -5022,18 +5030,22 @@ static ssize_t wq_numa_store(struct device *dev, struct device_attribute *attr, · 17 unmodified lines | ||
| 5022 | 5030 | { |
| 5023 | 5031 | struct workqueue_struct *wq = dev_to_wq(dev); |
| 5024 | 5032 | struct workqueue_attrs *attrs; |
| 5025 | − int v, ret; | |
| 5033 | + int v, ret = -ENOMEM; | |
| 5034 | + | |
| 5035 | + apply_wqattrs_lock(); | |
| 5026 | 5036 | |
| 5027 | 5037 | attrs = wq_sysfs_prep_attrs(wq); |
| 5028 | 5038 | if (!attrs) |
| 5029 | − return -ENOMEM; | |
| 5039 | + goto out_unlock; | |
| 5030 | 5040 | |
| 5031 | 5041 | ret = -EINVAL; |
| 5032 | 5042 | if (sscanf(buf, "%d", &v) == 1) { |
| 5033 | 5043 | attrs->no_numa = !v; |
| 5034 | − ret = apply_workqueue_attrs(wq, attrs); | |
| 5044 | + ret = apply_workqueue_attrs_locked(wq, attrs); | |
| 5035 | 5045 | } |
| 5036 | 5046 | |
| 5047 | +out_unlock: | |
| 5048 | + apply_wqattrs_unlock(); | |
| 5037 | 5049 | free_workqueue_attrs(attrs); |
| 5038 | 5050 | return ret ?: count; |
| 5039 | 5051 | } |