diff options
Diffstat (limited to 'drivers/s390/block')
-rw-r--r-- | drivers/s390/block/dasd.c | 36 | ||||
-rw-r--r-- | drivers/s390/block/dasd_3990_erp.c | 11 | ||||
-rw-r--r-- | drivers/s390/block/dasd_devmap.c | 13 | ||||
-rw-r--r-- | drivers/s390/block/dasd_diag.c | 6 | ||||
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 31 | ||||
-rw-r--r-- | drivers/s390/block/dasd_fba.c | 10 | ||||
-rw-r--r-- | drivers/s390/block/dasd_genhd.c | 3 | ||||
-rw-r--r-- | drivers/s390/block/dasd_int.h | 7 | ||||
-rw-r--r-- | drivers/s390/block/dasd_ioctl.c | 6 |
9 files changed, 95 insertions, 28 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..b3736b8aad3 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 */ @@ -2283,7 +2287,8 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) if (cqr->cpmode == 1) { cplength = 0; - datasize = sizeof(struct tcw) + sizeof(struct tsb); + /* TCW needs to be 64 byte aligned, so leave enough room */ + datasize = 64 + sizeof(struct tcw) + sizeof(struct tsb); } else { cplength = 2; datasize = 0; @@ -2312,8 +2317,8 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) if (cqr->cpmode == 1) { /* make a shallow copy of the original tcw but set new tsb */ erp->cpmode = 1; - erp->cpaddr = erp->data; - tcw = erp->data; + erp->cpaddr = PTR_ALIGN(erp->data, 64); + tcw = erp->cpaddr; tsb = (struct tsb *) &tcw[1]; *tcw = *((struct tcw *)cqr->cpaddr); tcw->tsb = (long)tsb; 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 1cca21aafab..0cb23311685 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: @@ -2839,8 +2845,13 @@ static int dasd_symm_io(struct dasd_device *device, void __user *argp) char *psf_data, *rssd_result; struct dasd_ccw_req *cqr; struct ccw1 *ccw; + char psf0, psf1; int rc; + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) + return -EACCES; + psf0 = psf1 = 0; + /* Copy parms from caller */ rc = -EFAULT; if (copy_from_user(&usrparm, argp, sizeof(usrparm))) @@ -2869,12 +2880,8 @@ static int dasd_symm_io(struct dasd_device *device, void __user *argp) (void __user *)(unsigned long) usrparm.psf_data, usrparm.psf_data_len)) goto out_free; - - /* sanity check on syscall header */ - if (psf_data[0] != 0x17 && psf_data[1] != 0xce) { - rc = -EINVAL; - goto out_free; - } + psf0 = psf_data[0]; + psf1 = psf_data[1]; /* setup CCWs for PSF + RSSD */ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 , 0, device); @@ -2925,7 +2932,9 @@ out_free: kfree(rssd_result); kfree(psf_data); out: - DBF_DEV_EVENT(DBF_WARNING, device, "Symmetrix ioctl: rc=%d", rc); + DBF_DEV_EVENT(DBF_WARNING, device, + "Symmetrix ioctl (0x%02x 0x%02x): rc=%d", + (int) psf0, (int) psf1, rc); return rc; } @@ -3146,11 +3155,11 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, tsb = NULL; sense = NULL; - if (irb->scsw.tm.tcw) + if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs == 0x01)) tsb = tcw_get_tsb( (struct tcw *)(unsigned long)irb->scsw.tm.tcw); - if (tsb && (irb->scsw.tm.fcxs == 0x01)) { + if (tsb) { len += sprintf(page + len, KERN_ERR PRINTK_HEADER " tsb->length %d\n", tsb->length); len += sprintf(page + len, KERN_ERR PRINTK_HEADER 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); } |