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 | struct workqueue_struct *wq = dev_to_wq(dev); | 4955 | struct workqueue_struct *wq = dev_to_wq(dev); |
| 4956 | struct workqueue_attrs *attrs; | 4956 | struct workqueue_attrs *attrs; |
| 4957 | int ret; | 4957 | int ret = -ENOMEM; |
| 4958 | |||
| 4959 | apply_wqattrs_lock(); | ||
| 4958 | 4960 | ||
| 4959 | attrs = wq_sysfs_prep_attrs(wq); | 4961 | attrs = wq_sysfs_prep_attrs(wq); |
| 4960 | if (!attrs) | 4962 | if (!attrs) |
| 4961 | return -ENOMEM; | 4963 | goto out_unlock; |
| 4962 | 4964 | ||
| 4963 | if (sscanf(buf, "%d", &attrs->nice) == 1 && | 4965 | if (sscanf(buf, "%d", &attrs->nice) == 1 && |
| 4964 | attrs->nice >= MIN_NICE && attrs->nice <= MAX_NICE) | 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 | else | 4968 | else |
| 4967 | ret = -EINVAL; | 4969 | ret = -EINVAL; |
| 4968 | 4970 | ||
| 4971 | out_unlock: | ||
| 4972 | apply_wqattrs_unlock(); | ||
| 4969 | free_workqueue_attrs(attrs); | 4973 | free_workqueue_attrs(attrs); |
| 4970 | return ret ?: count; | 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 | struct workqueue_struct *wq = dev_to_wq(dev); | 4994 | struct workqueue_struct *wq = dev_to_wq(dev); |
| 4991 | struct workqueue_attrs *attrs; | 4995 | struct workqueue_attrs *attrs; |
| 4992 | int ret; | 4996 | int ret = -ENOMEM; |
| 4997 | |||
| 4998 | apply_wqattrs_lock(); | ||
| 4993 | 4999 | ||
| 4994 | attrs = wq_sysfs_prep_attrs(wq); | 5000 | attrs = wq_sysfs_prep_attrs(wq); |
| 4995 | if (!attrs) | 5001 | if (!attrs) |
| 4996 | return -ENOMEM; | 5002 | goto out_unlock; |
| 4997 | 5003 | ||
| 4998 | ret = cpumask_parse(buf, attrs->cpumask); | 5004 | ret = cpumask_parse(buf, attrs->cpumask); |
| 4999 | if (!ret) | 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 | free_workqueue_attrs(attrs); | 5010 | free_workqueue_attrs(attrs); |
| 5003 | return ret ?: count; | 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 | struct workqueue_struct *wq = dev_to_wq(dev); | 5031 | struct workqueue_struct *wq = dev_to_wq(dev); |
| 5024 | struct workqueue_attrs *attrs; | 5032 | struct workqueue_attrs *attrs; |
| 5025 | int v, ret; | 5033 | int v, ret = -ENOMEM; |
| 5034 | |||
| 5035 | apply_wqattrs_lock(); | ||
| 5026 | 5036 | ||
| 5027 | attrs = wq_sysfs_prep_attrs(wq); | 5037 | attrs = wq_sysfs_prep_attrs(wq); |
| 5028 | if (!attrs) | 5038 | if (!attrs) |
| 5029 | return -ENOMEM; | 5039 | goto out_unlock; |
| 5030 | 5040 | ||
| 5031 | ret = -EINVAL; | 5041 | ret = -EINVAL; |
| 5032 | if (sscanf(buf, "%d", &v) == 1) { | 5042 | if (sscanf(buf, "%d", &v) == 1) { |
| 5033 | attrs->no_numa = !v; | 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 | free_workqueue_attrs(attrs); | 5049 | free_workqueue_attrs(attrs); |
| 5038 | return ret ?: count; | 5050 | return ret ?: count; |
| 5039 | } | 5051 | } |