Ver código fonte

s390/cio: use device_lock during cmb activation

Hold the device_lock during [de]activation of the channel measurement
block to synchronize concurrent usage of these functions.

Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Reviewed-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Sebastian Ott 10 anos atrás
pai
commit
1bc6664bdf
3 arquivos alterados com 39 adições e 13 exclusões
  1. 1 0
      arch/s390/include/asm/cmb.h
  2. 37 12
      drivers/s390/cio/cmf.c
  3. 1 1
      drivers/s390/cio/device.c

+ 1 - 0
arch/s390/include/asm/cmb.h

@@ -6,6 +6,7 @@
 struct ccw_device;
 extern int enable_cmf(struct ccw_device *cdev);
 extern int disable_cmf(struct ccw_device *cdev);
+extern int __disable_cmf(struct ccw_device *cdev);
 extern u64 cmf_read(struct ccw_device *cdev, int index);
 extern int cmf_readall(struct ccw_device *cdev, struct cmbdata *data);
 

+ 37 - 12
drivers/s390/cio/cmf.c

@@ -1226,41 +1226,66 @@ int enable_cmf(struct ccw_device *cdev)
 {
 	int ret;
 
+	device_lock(&cdev->dev);
 	ret = cmbops->alloc(cdev);
-	cmbops->reset(cdev);
 	if (ret)
-		return ret;
+		goto out;
+	cmbops->reset(cdev);
+	ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group);
+	if (ret) {
+		cmbops->free(cdev);
+		goto out;
+	}
 	ret = cmbops->set(cdev, 2);
 	if (ret) {
+		sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group);
 		cmbops->free(cdev);
-		return ret;
 	}
-	ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group);
-	if (!ret)
-		return 0;
-	cmbops->set(cdev, 0);  //FIXME: this can fail
-	cmbops->free(cdev);
+out:
+	device_unlock(&cdev->dev);
 	return ret;
 }
 
 /**
- * disable_cmf() - switch off the channel measurement for a specific device
+ * __disable_cmf() - switch off the channel measurement for a specific device
  *  @cdev:	The ccw device to be disabled
  *
  *  Returns %0 for success or a negative error value.
  *
  *  Context:
- *    non-atomic
+ *    non-atomic, device_lock() held.
  */
-int disable_cmf(struct ccw_device *cdev)
+int __disable_cmf(struct ccw_device *cdev)
 {
 	int ret;
 
 	ret = cmbops->set(cdev, 0);
 	if (ret)
 		return ret;
-	cmbops->free(cdev);
+
 	sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group);
+	cmbops->free(cdev);
+
+	return ret;
+}
+
+/**
+ * disable_cmf() - switch off the channel measurement for a specific device
+ *  @cdev:	The ccw device to be disabled
+ *
+ *  Returns %0 for success or a negative error value.
+ *
+ *  Context:
+ *    non-atomic
+ */
+int disable_cmf(struct ccw_device *cdev)
+{
+	int ret;
+
+	device_lock(&cdev->dev);
+	ret = __disable_cmf(cdev);
+	device_unlock(&cdev->dev);
+
 	return ret;
 }
 

+ 1 - 1
drivers/s390/cio/device.c

@@ -1797,7 +1797,7 @@ static void ccw_device_shutdown(struct device *dev)
 	cdev = to_ccwdev(dev);
 	if (cdev->drv && cdev->drv->shutdown)
 		cdev->drv->shutdown(cdev);
-	disable_cmf(cdev);
+	__disable_cmf(cdev);
 }
 
 static int ccw_device_pm_prepare(struct device *dev)