[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>

authored by Stefan Weinhuber and committed by Martin Schwidefsky 33b62a30 584dfddf

+79 -16
+36
drivers/s390/block/dasd.c
··· 26 #include <asm/ebcdic.h> 27 #include <asm/idals.h> 28 #include <asm/itcw.h> 29 30 /* This is ugly... */ 31 #define PRINTK_HEADER "dasd:" ··· 2213 goto out; 2214 } 2215 2216 return 0; 2217 2218 out: ··· 2296 /* 2297 * SECTION: common functions for ccw_driver use 2298 */ 2299 2300 static void dasd_generic_auto_online(void *data, async_cookie_t cookie) 2301 {
··· 26 #include <asm/ebcdic.h> 27 #include <asm/idals.h> 28 #include <asm/itcw.h> 29 + #include <asm/diag.h> 30 31 /* This is ugly... */ 32 #define PRINTK_HEADER "dasd:" ··· 2212 goto out; 2213 } 2214 2215 + if ((mode & FMODE_WRITE) && 2216 + (test_bit(DASD_FLAG_DEVICE_RO, &base->flags) || 2217 + (base->features & DASD_FEATURE_READONLY))) { 2218 + rc = -EROFS; 2219 + goto out; 2220 + } 2221 + 2222 return 0; 2223 2224 out: ··· 2288 /* 2289 * SECTION: common functions for ccw_driver use 2290 */ 2291 + 2292 + /* 2293 + * Is the device read-only? 2294 + * Note that this function does not report the setting of the 2295 + * readonly device attribute, but how it is configured in z/VM. 2296 + */ 2297 + int dasd_device_is_ro(struct dasd_device *device) 2298 + { 2299 + struct ccw_dev_id dev_id; 2300 + struct diag210 diag_data; 2301 + int rc; 2302 + 2303 + if (!MACHINE_IS_VM) 2304 + return 0; 2305 + ccw_device_get_id(device->cdev, &dev_id); 2306 + memset(&diag_data, 0, sizeof(diag_data)); 2307 + diag_data.vrdcdvno = dev_id.devno; 2308 + diag_data.vrdclen = sizeof(diag_data); 2309 + rc = diag210(&diag_data); 2310 + if (rc == 0 || rc == 2) { 2311 + return diag_data.vrdcvfla & 0x80; 2312 + } else { 2313 + DBF_EVENT(DBF_WARNING, "diag210 failed for dev=%04x with rc=%d", 2314 + dev_id.devno, rc); 2315 + return 0; 2316 + } 2317 + } 2318 + EXPORT_SYMBOL_GPL(dasd_device_is_ro); 2319 2320 static void dasd_generic_auto_online(void *data, async_cookie_t cookie) 2321 {
+4
drivers/s390/block/dasd_3990_erp.c
··· 1045 1046 erp->retries = 5; 1047 1048 } else { 1049 /* fatal error - set status to FAILED 1050 internal error 09 - Command Reject */
··· 1045 1046 erp->retries = 5; 1047 1048 + } else if (sense[1] & SNS1_WRITE_INHIBITED) { 1049 + dev_err(&device->cdev->dev, "An I/O request was rejected" 1050 + " because writing is inhibited\n"); 1051 + erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); 1052 } else { 1053 /* fatal error - set status to FAILED 1054 internal error 09 - Command Reject */
+8 -5
drivers/s390/block/dasd_devmap.c
··· 742 const char *buf, size_t count) 743 { 744 struct dasd_devmap *devmap; 745 int val; 746 char *endp; 747 ··· 759 devmap->features |= DASD_FEATURE_READONLY; 760 else 761 devmap->features &= ~DASD_FEATURE_READONLY; 762 - if (devmap->device) 763 - devmap->device->features = devmap->features; 764 - if (devmap->device && devmap->device->block 765 - && devmap->device->block->gdp) 766 - set_disk_ro(devmap->device->block->gdp, val); 767 spin_unlock(&dasd_devmap_lock); 768 return count; 769 } 770
··· 742 const char *buf, size_t count) 743 { 744 struct dasd_devmap *devmap; 745 + struct dasd_device *device; 746 int val; 747 char *endp; 748 ··· 758 devmap->features |= DASD_FEATURE_READONLY; 759 else 760 devmap->features &= ~DASD_FEATURE_READONLY; 761 + device = devmap->device; 762 + if (device) { 763 + device->features = devmap->features; 764 + val = val || test_bit(DASD_FLAG_DEVICE_RO, &device->flags); 765 + } 766 spin_unlock(&dasd_devmap_lock); 767 + if (device && device->block && device->block->gdp) 768 + set_disk_ro(device->block->gdp, val); 769 return count; 770 } 771
+2 -4
drivers/s390/block/dasd_diag.c
··· 145 mdsk_term_io(device); 146 rc = mdsk_init_io(device, device->block->bp_block, 0, NULL); 147 if (rc == 4) { 148 - if (!(device->features & DASD_FEATURE_READONLY)) { 149 pr_warning("%s: The access mode of a DIAG device " 150 "changed to read-only\n", 151 dev_name(&device->cdev->dev)); 152 - device->features |= DASD_FEATURE_READONLY; 153 - } 154 rc = 0; 155 } 156 if (rc) ··· 447 rc = -EIO; 448 } else { 449 if (rc == 4) 450 - device->features |= DASD_FEATURE_READONLY; 451 pr_info("%s: New DASD with %ld byte/block, total size %ld " 452 "KB%s\n", dev_name(&device->cdev->dev), 453 (unsigned long) block->bp_block,
··· 145 mdsk_term_io(device); 146 rc = mdsk_init_io(device, device->block->bp_block, 0, NULL); 147 if (rc == 4) { 148 + if (!(test_and_set_bit(DASD_FLAG_DEVICE_RO, &device->flags))) 149 pr_warning("%s: The access mode of a DIAG device " 150 "changed to read-only\n", 151 dev_name(&device->cdev->dev)); 152 rc = 0; 153 } 154 if (rc) ··· 449 rc = -EIO; 450 } else { 451 if (rc == 4) 452 + set_bit(DASD_FLAG_DEVICE_RO, &device->flags); 453 pr_info("%s: New DASD with %ld byte/block, total size %ld " 454 "KB%s\n", dev_name(&device->cdev->dev), 455 (unsigned long) block->bp_block,
+8 -2
drivers/s390/block/dasd_eckd.c
··· 1089 struct dasd_eckd_private *private; 1090 struct dasd_block *block; 1091 int is_known, rc; 1092 1093 if (!ccw_device_is_pathgroup(device->cdev)) { 1094 dev_warn(&device->cdev->dev, ··· 1183 else 1184 private->real_cyl = private->rdc_data.no_cyl; 1185 1186 dev_info(&device->cdev->dev, "New DASD %04X/%02X (CU %04X/%02X) " 1187 - "with %d cylinders, %d heads, %d sectors\n", 1188 private->rdc_data.dev_type, 1189 private->rdc_data.dev_model, 1190 private->rdc_data.cu_type, 1191 private->rdc_data.cu_model.model, 1192 private->real_cyl, 1193 private->rdc_data.trk_per_cyl, 1194 - private->rdc_data.sec_per_trk); 1195 return 0; 1196 1197 out_err3:
··· 1089 struct dasd_eckd_private *private; 1090 struct dasd_block *block; 1091 int is_known, rc; 1092 + int readonly; 1093 1094 if (!ccw_device_is_pathgroup(device->cdev)) { 1095 dev_warn(&device->cdev->dev, ··· 1182 else 1183 private->real_cyl = private->rdc_data.no_cyl; 1184 1185 + readonly = dasd_device_is_ro(device); 1186 + if (readonly) 1187 + set_bit(DASD_FLAG_DEVICE_RO, &device->flags); 1188 + 1189 dev_info(&device->cdev->dev, "New DASD %04X/%02X (CU %04X/%02X) " 1190 + "with %d cylinders, %d heads, %d sectors%s\n", 1191 private->rdc_data.dev_type, 1192 private->rdc_data.dev_model, 1193 private->rdc_data.cu_type, 1194 private->rdc_data.cu_model.model, 1195 private->real_cyl, 1196 private->rdc_data.trk_per_cyl, 1197 + private->rdc_data.sec_per_trk, 1198 + readonly ? ", read-only device" : ""); 1199 return 0; 1200 1201 out_err3:
+8 -2
drivers/s390/block/dasd_fba.c
··· 124 struct dasd_fba_private *private; 125 struct ccw_device *cdev = device->cdev; 126 int rc; 127 128 private = (struct dasd_fba_private *) device->private; 129 if (!private) { ··· 163 return rc; 164 } 165 166 dev_info(&device->cdev->dev, 167 "New FBA DASD %04X/%02X (CU %04X/%02X) with %d MB " 168 - "and %d B/blk\n", 169 cdev->id.dev_type, 170 cdev->id.dev_model, 171 cdev->id.cu_type, 172 cdev->id.cu_model, 173 ((private->rdc_data.blk_bdsa * 174 (private->rdc_data.blk_size >> 9)) >> 11), 175 - private->rdc_data.blk_size); 176 return 0; 177 } 178
··· 124 struct dasd_fba_private *private; 125 struct ccw_device *cdev = device->cdev; 126 int rc; 127 + int readonly; 128 129 private = (struct dasd_fba_private *) device->private; 130 if (!private) { ··· 162 return rc; 163 } 164 165 + readonly = dasd_device_is_ro(device); 166 + if (readonly) 167 + set_bit(DASD_FLAG_DEVICE_RO, &device->flags); 168 + 169 dev_info(&device->cdev->dev, 170 "New FBA DASD %04X/%02X (CU %04X/%02X) with %d MB " 171 + "and %d B/blk%s\n", 172 cdev->id.dev_type, 173 cdev->id.dev_model, 174 cdev->id.cu_type, 175 cdev->id.cu_model, 176 ((private->rdc_data.blk_bdsa * 177 (private->rdc_data.blk_size >> 9)) >> 11), 178 + private->rdc_data.blk_size, 179 + readonly ? ", read-only device" : ""); 180 return 0; 181 } 182
+2 -1
drivers/s390/block/dasd_genhd.c
··· 70 } 71 len += sprintf(gdp->disk_name + len, "%c", 'a'+(base->devindex%26)); 72 73 - if (block->base->features & DASD_FEATURE_READONLY) 74 set_disk_ro(gdp, 1); 75 gdp->private_data = block; 76 gdp->queue = block->request_queue;
··· 70 } 71 len += sprintf(gdp->disk_name + len, "%c", 'a'+(base->devindex%26)); 72 73 + if (base->features & DASD_FEATURE_READONLY || 74 + test_bit(DASD_FLAG_DEVICE_RO, &base->flags)) 75 set_disk_ro(gdp, 1); 76 gdp->private_data = block; 77 gdp->queue = block->request_queue;
+7
drivers/s390/block/dasd_int.h
··· 436 #define DASD_FLAG_OFFLINE 3 /* device is in offline processing */ 437 #define DASD_FLAG_EER_SNSS 4 /* A SNSS is required */ 438 #define DASD_FLAG_EER_IN_USE 5 /* A SNSS request is running */ 439 440 void dasd_put_device_wake(struct dasd_device *); 441 ··· 612 613 void dasd_device_set_stop_bits(struct dasd_device *, int); 614 void dasd_device_remove_stop_bits(struct dasd_device *, int); 615 616 /* externals in dasd_devmap.c */ 617 extern int dasd_max_devindex;
··· 436 #define DASD_FLAG_OFFLINE 3 /* device is in offline processing */ 437 #define DASD_FLAG_EER_SNSS 4 /* A SNSS is required */ 438 #define DASD_FLAG_EER_IN_USE 5 /* A SNSS request is running */ 439 + #define DASD_FLAG_DEVICE_RO 6 /* The device itself is read-only. Don't 440 + * confuse this with the user specified 441 + * read-only feature. 442 + */ 443 444 void dasd_put_device_wake(struct dasd_device *); 445 ··· 608 609 void dasd_device_set_stop_bits(struct dasd_device *, int); 610 void dasd_device_remove_stop_bits(struct dasd_device *, int); 611 + 612 + int dasd_device_is_ro(struct dasd_device *); 613 + 614 615 /* externals in dasd_devmap.c */ 616 extern int dasd_max_devindex;
+4 -2
drivers/s390/block/dasd_ioctl.c
··· 199 if (!argp) 200 return -EINVAL; 201 202 - if (block->base->features & DASD_FEATURE_READONLY) 203 return -EROFS; 204 if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) 205 return -EFAULT; ··· 350 return -EINVAL; 351 if (get_user(intval, (int __user *)argp)) 352 return -EFAULT; 353 - 354 set_disk_ro(bdev->bd_disk, intval); 355 return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval); 356 }
··· 199 if (!argp) 200 return -EINVAL; 201 202 + if (block->base->features & DASD_FEATURE_READONLY || 203 + test_bit(DASD_FLAG_DEVICE_RO, &block->base->flags)) 204 return -EROFS; 205 if (copy_from_user(&fdata, argp, sizeof(struct format_data_t))) 206 return -EFAULT; ··· 349 return -EINVAL; 350 if (get_user(intval, (int __user *)argp)) 351 return -EFAULT; 352 + if (!intval && test_bit(DASD_FLAG_DEVICE_RO, &block->base->flags)) 353 + return -EROFS; 354 set_disk_ro(bdev->bd_disk, intval); 355 return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval); 356 }