summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Weinhuber <wein@de.ibm.com>2010-03-08 12:26:24 +0100
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2010-03-08 12:26:27 +0100
commit33b62a30f78536b976183cc764c08038ac011e0a (patch)
tree60a18b4478c68082835fa6fee9a24bb1e7e8b312
parent584dfddfce1bd81d877f14de2b56d4bc747af821 (diff)
downloadlinux-3.10-33b62a30f78536b976183cc764c08038ac011e0a.tar.gz
linux-3.10-33b62a30f78536b976183cc764c08038ac011e0a.tar.bz2
linux-3.10-33b62a30f78536b976183cc764c08038ac011e0a.zip
[S390] dasd: automatic recognition of read-only devices
In z/VM it is possible to attach a device as read-only. To prevent unintentional write requests and subsequent I/O errors, we can detect this configuration using the z/VM DIAG 210 interface and set the respective linux block device to read-only as well. Signed-off-by: Stefan Weinhuber <wein@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r--drivers/s390/block/dasd.c36
-rw-r--r--drivers/s390/block/dasd_3990_erp.c4
-rw-r--r--drivers/s390/block/dasd_devmap.c13
-rw-r--r--drivers/s390/block/dasd_diag.c6
-rw-r--r--drivers/s390/block/dasd_eckd.c10
-rw-r--r--drivers/s390/block/dasd_fba.c10
-rw-r--r--drivers/s390/block/dasd_genhd.c3
-rw-r--r--drivers/s390/block/dasd_int.h7
-rw-r--r--drivers/s390/block/dasd_ioctl.c6
9 files changed, 79 insertions, 16 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 4951aa82e9f..bbea90baf98 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -26,6 +26,7 @@
#include <asm/ebcdic.h>
#include <asm/idals.h>
#include <asm/itcw.h>
+#include <asm/diag.h>
/* This is ugly... */
#define PRINTK_HEADER "dasd:"
@@ -2212,6 +2213,13 @@ static int dasd_open(struct block_device *bdev, fmode_t mode)
goto out;
}
+ if ((mode & FMODE_WRITE) &&
+ (test_bit(DASD_FLAG_DEVICE_RO, &base->flags) ||
+ (base->features & DASD_FEATURE_READONLY))) {
+ rc = -EROFS;
+ goto out;
+ }
+
return 0;
out:
@@ -2289,6 +2297,34 @@ dasd_exit(void)
* SECTION: common functions for ccw_driver use
*/
+/*
+ * Is the device read-only?
+ * Note that this function does not report the setting of the
+ * readonly device attribute, but how it is configured in z/VM.
+ */
+int dasd_device_is_ro(struct dasd_device *device)
+{
+ struct ccw_dev_id dev_id;
+ struct diag210 diag_data;
+ int rc;
+
+ if (!MACHINE_IS_VM)
+ return 0;
+ ccw_device_get_id(device->cdev, &dev_id);
+ memset(&diag_data, 0, sizeof(diag_data));
+ diag_data.vrdcdvno = dev_id.devno;
+ diag_data.vrdclen = sizeof(diag_data);
+ rc = diag210(&diag_data);
+ if (rc == 0 || rc == 2) {
+ return diag_data.vrdcvfla & 0x80;
+ } else {
+ DBF_EVENT(DBF_WARNING, "diag210 failed for dev=%04x with rc=%d",
+ dev_id.devno, rc);
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(dasd_device_is_ro);
+
static void dasd_generic_auto_online(void *data, async_cookie_t cookie)
{
struct ccw_device *cdev = data;
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index 44796ba4eb9..51224f76b98 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -1045,6 +1045,10 @@ dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense)
erp->retries = 5;
+ } else if (sense[1] & SNS1_WRITE_INHIBITED) {
+ dev_err(&device->cdev->dev, "An I/O request was rejected"
+ " because writing is inhibited\n");
+ erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED);
} else {
/* fatal error - set status to FAILED
internal error 09 - Command Reject */
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index d49766f3b94..8e23919c870 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -742,6 +742,7 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct dasd_devmap *devmap;
+ struct dasd_device *device;
int val;
char *endp;
@@ -758,12 +759,14 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
devmap->features |= DASD_FEATURE_READONLY;
else
devmap->features &= ~DASD_FEATURE_READONLY;
- if (devmap->device)
- devmap->device->features = devmap->features;
- if (devmap->device && devmap->device->block
- && devmap->device->block->gdp)
- set_disk_ro(devmap->device->block->gdp, val);
+ device = devmap->device;
+ if (device) {
+ device->features = devmap->features;
+ val = val || test_bit(DASD_FLAG_DEVICE_RO, &device->flags);
+ }
spin_unlock(&dasd_devmap_lock);
+ if (device && device->block && device->block->gdp)
+ set_disk_ro(device->block->gdp, val);
return count;
}
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 6e14863f5c7..687f323cdc3 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -145,12 +145,10 @@ dasd_diag_erp(struct dasd_device *device)
mdsk_term_io(device);
rc = mdsk_init_io(device, device->block->bp_block, 0, NULL);
if (rc == 4) {
- if (!(device->features & DASD_FEATURE_READONLY)) {
+ if (!(test_and_set_bit(DASD_FLAG_DEVICE_RO, &device->flags)))
pr_warning("%s: The access mode of a DIAG device "
"changed to read-only\n",
dev_name(&device->cdev->dev));
- device->features |= DASD_FEATURE_READONLY;
- }
rc = 0;
}
if (rc)
@@ -449,7 +447,7 @@ dasd_diag_check_device(struct dasd_device *device)
rc = -EIO;
} else {
if (rc == 4)
- device->features |= DASD_FEATURE_READONLY;
+ set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
pr_info("%s: New DASD with %ld byte/block, total size %ld "
"KB%s\n", dev_name(&device->cdev->dev),
(unsigned long) block->bp_block,
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index d7163f904f4..01f4e7a34aa 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1089,6 +1089,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
struct dasd_eckd_private *private;
struct dasd_block *block;
int is_known, rc;
+ int readonly;
if (!ccw_device_is_pathgroup(device->cdev)) {
dev_warn(&device->cdev->dev,
@@ -1182,15 +1183,20 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
else
private->real_cyl = private->rdc_data.no_cyl;
+ readonly = dasd_device_is_ro(device);
+ if (readonly)
+ set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
+
dev_info(&device->cdev->dev, "New DASD %04X/%02X (CU %04X/%02X) "
- "with %d cylinders, %d heads, %d sectors\n",
+ "with %d cylinders, %d heads, %d sectors%s\n",
private->rdc_data.dev_type,
private->rdc_data.dev_model,
private->rdc_data.cu_type,
private->rdc_data.cu_model.model,
private->real_cyl,
private->rdc_data.trk_per_cyl,
- private->rdc_data.sec_per_trk);
+ private->rdc_data.sec_per_trk,
+ readonly ? ", read-only device" : "");
return 0;
out_err3:
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index 0f152444ac7..37282b90eec 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -124,6 +124,7 @@ dasd_fba_check_characteristics(struct dasd_device *device)
struct dasd_fba_private *private;
struct ccw_device *cdev = device->cdev;
int rc;
+ int readonly;
private = (struct dasd_fba_private *) device->private;
if (!private) {
@@ -162,16 +163,21 @@ dasd_fba_check_characteristics(struct dasd_device *device)
return rc;
}
+ readonly = dasd_device_is_ro(device);
+ if (readonly)
+ set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
+
dev_info(&device->cdev->dev,
"New FBA DASD %04X/%02X (CU %04X/%02X) with %d MB "
- "and %d B/blk\n",
+ "and %d B/blk%s\n",
cdev->id.dev_type,
cdev->id.dev_model,
cdev->id.cu_type,
cdev->id.cu_model,
((private->rdc_data.blk_bdsa *
(private->rdc_data.blk_size >> 9)) >> 11),
- private->rdc_data.blk_size);
+ private->rdc_data.blk_size,
+ readonly ? ", read-only device" : "");
return 0;
}
diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c
index 94f92a1247f..30a1ca3d08b 100644
--- a/drivers/s390/block/dasd_genhd.c
+++ b/drivers/s390/block/dasd_genhd.c
@@ -70,7 +70,8 @@ int dasd_gendisk_alloc(struct dasd_block *block)
}
len += sprintf(gdp->disk_name + len, "%c", 'a'+(base->devindex%26));
- if (block->base->features & DASD_FEATURE_READONLY)
+ if (base->features & DASD_FEATURE_READONLY ||
+ test_bit(DASD_FLAG_DEVICE_RO, &base->flags))
set_disk_ro(gdp, 1);
gdp->private_data = block;
gdp->queue = block->request_queue;
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index ed73ce55082..a91d4a97d4f 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -436,6 +436,10 @@ struct dasd_block {
#define DASD_FLAG_OFFLINE 3 /* device is in offline processing */
#define DASD_FLAG_EER_SNSS 4 /* A SNSS is required */
#define DASD_FLAG_EER_IN_USE 5 /* A SNSS request is running */
+#define DASD_FLAG_DEVICE_RO 6 /* The device itself is read-only. Don't
+ * confuse this with the user specified
+ * read-only feature.
+ */
void dasd_put_device_wake(struct dasd_device *);
@@ -609,6 +613,9 @@ char *dasd_get_sense(struct irb *);
void dasd_device_set_stop_bits(struct dasd_device *, int);
void dasd_device_remove_stop_bits(struct dasd_device *, int);
+int dasd_device_is_ro(struct dasd_device *);
+
+
/* externals in dasd_devmap.c */
extern int dasd_max_devindex;
extern int dasd_probeonly;
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index 7039d9cf0fb..3479f8158a1 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -199,7 +199,8 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp)
if (!argp)
return -EINVAL;
- if (block->base->features & DASD_FEATURE_READONLY)
+ if (block->base->features & DASD_FEATURE_READONLY ||
+ test_bit(DASD_FLAG_DEVICE_RO, &block->base->flags))
return -EROFS;
if (copy_from_user(&fdata, argp, sizeof(struct format_data_t)))
return -EFAULT;
@@ -349,7 +350,8 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
return -EINVAL;
if (get_user(intval, (int __user *)argp))
return -EFAULT;
-
+ if (!intval && test_bit(DASD_FLAG_DEVICE_RO, &block->base->flags))
+ return -EROFS;
set_disk_ro(bdev->bd_disk, intval);
return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval);
}