Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

s390/cio: allow to reset channel measurement block

Prior to commit 1bc6664bdfb949bc69a08113801e7d6acbf6bc3f a call to
enable_cmf for a device for which channel measurement was already
enabled resulted in a reset of the measurement data.

What looked like bugs at the time (a 2nd allocation was triggered
but failed, reset was called regardless of previous failures, and
errors have not been reported to userspace) was actually something
at least one userspace tool depended on. Restore that behavior in
a sane way.

Fixes: 1bc6664bdfb ("s390/cio: use device_lock during cmb activation")
Cc: stable@vger.kernel.org #v4.4+
Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Reviewed-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Reviewed-by: Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

authored by

Sebastian Ott and committed by
Martin Schwidefsky
0f5d050c 8f50af49

+20 -9
+20 -9
drivers/s390/cio/cmf.c
··· 756 756 cmf_generic_reset(cdev); 757 757 } 758 758 759 + static int cmf_enabled(struct ccw_device *cdev) 760 + { 761 + int enabled; 762 + 763 + spin_lock_irq(cdev->ccwlock); 764 + enabled = !!cdev->private->cmb; 765 + spin_unlock_irq(cdev->ccwlock); 766 + 767 + return enabled; 768 + } 769 + 759 770 static struct attribute_group cmf_attr_group; 760 771 761 772 static struct cmb_operations cmbops_basic = { ··· 1167 1156 char *buf) 1168 1157 { 1169 1158 struct ccw_device *cdev = to_ccwdev(dev); 1170 - int enabled; 1171 1159 1172 - spin_lock_irq(cdev->ccwlock); 1173 - enabled = !!cdev->private->cmb; 1174 - spin_unlock_irq(cdev->ccwlock); 1175 - 1176 - return sprintf(buf, "%d\n", enabled); 1160 + return sprintf(buf, "%d\n", cmf_enabled(cdev)); 1177 1161 } 1178 1162 1179 1163 static ssize_t cmb_enable_store(struct device *dev, ··· 1208 1202 * @cdev: The ccw device to be enabled 1209 1203 * 1210 1204 * Returns %0 for success or a negative error value. 1211 - * 1205 + * Note: If this is called on a device for which channel measurement is already 1206 + * enabled a reset of the measurement data is triggered. 1212 1207 * Context: 1213 1208 * non-atomic 1214 1209 */ 1215 1210 int enable_cmf(struct ccw_device *cdev) 1216 1211 { 1217 - int ret; 1212 + int ret = 0; 1218 1213 1219 1214 device_lock(&cdev->dev); 1215 + if (cmf_enabled(cdev)) { 1216 + cmbops->reset(cdev); 1217 + goto out_unlock; 1218 + } 1220 1219 get_device(&cdev->dev); 1221 1220 ret = cmbops->alloc(cdev); 1222 1221 if (ret) ··· 1240 1229 out: 1241 1230 if (ret) 1242 1231 put_device(&cdev->dev); 1243 - 1232 + out_unlock: 1244 1233 device_unlock(&cdev->dev); 1245 1234 return ret; 1246 1235 }