[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>
This commit is contained in:
parent
584dfddfce
commit
33b62a30f7
|
@ -26,6 +26,7 @@
|
||||||
#include <asm/ebcdic.h>
|
#include <asm/ebcdic.h>
|
||||||
#include <asm/idals.h>
|
#include <asm/idals.h>
|
||||||
#include <asm/itcw.h>
|
#include <asm/itcw.h>
|
||||||
|
#include <asm/diag.h>
|
||||||
|
|
||||||
/* This is ugly... */
|
/* This is ugly... */
|
||||||
#define PRINTK_HEADER "dasd:"
|
#define PRINTK_HEADER "dasd:"
|
||||||
|
@ -2212,6 +2213,13 @@ static int dasd_open(struct block_device *bdev, fmode_t mode)
|
||||||
goto out;
|
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;
|
return 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -2289,6 +2297,34 @@ dasd_exit(void)
|
||||||
* SECTION: common functions for ccw_driver use
|
* 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)
|
static void dasd_generic_auto_online(void *data, async_cookie_t cookie)
|
||||||
{
|
{
|
||||||
struct ccw_device *cdev = data;
|
struct ccw_device *cdev = data;
|
||||||
|
|
|
@ -1045,6 +1045,10 @@ dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense)
|
||||||
|
|
||||||
erp->retries = 5;
|
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 {
|
} else {
|
||||||
/* fatal error - set status to FAILED
|
/* fatal error - set status to FAILED
|
||||||
internal error 09 - Command Reject */
|
internal error 09 - Command Reject */
|
||||||
|
|
|
@ -742,6 +742,7 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct dasd_devmap *devmap;
|
struct dasd_devmap *devmap;
|
||||||
|
struct dasd_device *device;
|
||||||
int val;
|
int val;
|
||||||
char *endp;
|
char *endp;
|
||||||
|
|
||||||
|
@ -758,12 +759,14 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
|
||||||
devmap->features |= DASD_FEATURE_READONLY;
|
devmap->features |= DASD_FEATURE_READONLY;
|
||||||
else
|
else
|
||||||
devmap->features &= ~DASD_FEATURE_READONLY;
|
devmap->features &= ~DASD_FEATURE_READONLY;
|
||||||
if (devmap->device)
|
device = devmap->device;
|
||||||
devmap->device->features = devmap->features;
|
if (device) {
|
||||||
if (devmap->device && devmap->device->block
|
device->features = devmap->features;
|
||||||
&& devmap->device->block->gdp)
|
val = val || test_bit(DASD_FLAG_DEVICE_RO, &device->flags);
|
||||||
set_disk_ro(devmap->device->block->gdp, val);
|
}
|
||||||
spin_unlock(&dasd_devmap_lock);
|
spin_unlock(&dasd_devmap_lock);
|
||||||
|
if (device && device->block && device->block->gdp)
|
||||||
|
set_disk_ro(device->block->gdp, val);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,12 +145,10 @@ dasd_diag_erp(struct dasd_device *device)
|
||||||
mdsk_term_io(device);
|
mdsk_term_io(device);
|
||||||
rc = mdsk_init_io(device, device->block->bp_block, 0, NULL);
|
rc = mdsk_init_io(device, device->block->bp_block, 0, NULL);
|
||||||
if (rc == 4) {
|
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 "
|
pr_warning("%s: The access mode of a DIAG device "
|
||||||
"changed to read-only\n",
|
"changed to read-only\n",
|
||||||
dev_name(&device->cdev->dev));
|
dev_name(&device->cdev->dev));
|
||||||
device->features |= DASD_FEATURE_READONLY;
|
|
||||||
}
|
|
||||||
rc = 0;
|
rc = 0;
|
||||||
}
|
}
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -449,7 +447,7 @@ dasd_diag_check_device(struct dasd_device *device)
|
||||||
rc = -EIO;
|
rc = -EIO;
|
||||||
} else {
|
} else {
|
||||||
if (rc == 4)
|
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 "
|
pr_info("%s: New DASD with %ld byte/block, total size %ld "
|
||||||
"KB%s\n", dev_name(&device->cdev->dev),
|
"KB%s\n", dev_name(&device->cdev->dev),
|
||||||
(unsigned long) block->bp_block,
|
(unsigned long) block->bp_block,
|
||||||
|
|
|
@ -1089,6 +1089,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
|
||||||
struct dasd_eckd_private *private;
|
struct dasd_eckd_private *private;
|
||||||
struct dasd_block *block;
|
struct dasd_block *block;
|
||||||
int is_known, rc;
|
int is_known, rc;
|
||||||
|
int readonly;
|
||||||
|
|
||||||
if (!ccw_device_is_pathgroup(device->cdev)) {
|
if (!ccw_device_is_pathgroup(device->cdev)) {
|
||||||
dev_warn(&device->cdev->dev,
|
dev_warn(&device->cdev->dev,
|
||||||
|
@ -1182,15 +1183,20 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
|
||||||
else
|
else
|
||||||
private->real_cyl = private->rdc_data.no_cyl;
|
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) "
|
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_type,
|
||||||
private->rdc_data.dev_model,
|
private->rdc_data.dev_model,
|
||||||
private->rdc_data.cu_type,
|
private->rdc_data.cu_type,
|
||||||
private->rdc_data.cu_model.model,
|
private->rdc_data.cu_model.model,
|
||||||
private->real_cyl,
|
private->real_cyl,
|
||||||
private->rdc_data.trk_per_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;
|
return 0;
|
||||||
|
|
||||||
out_err3:
|
out_err3:
|
||||||
|
|
|
@ -124,6 +124,7 @@ dasd_fba_check_characteristics(struct dasd_device *device)
|
||||||
struct dasd_fba_private *private;
|
struct dasd_fba_private *private;
|
||||||
struct ccw_device *cdev = device->cdev;
|
struct ccw_device *cdev = device->cdev;
|
||||||
int rc;
|
int rc;
|
||||||
|
int readonly;
|
||||||
|
|
||||||
private = (struct dasd_fba_private *) device->private;
|
private = (struct dasd_fba_private *) device->private;
|
||||||
if (!private) {
|
if (!private) {
|
||||||
|
@ -162,16 +163,21 @@ dasd_fba_check_characteristics(struct dasd_device *device)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readonly = dasd_device_is_ro(device);
|
||||||
|
if (readonly)
|
||||||
|
set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
|
||||||
|
|
||||||
dev_info(&device->cdev->dev,
|
dev_info(&device->cdev->dev,
|
||||||
"New FBA DASD %04X/%02X (CU %04X/%02X) with %d MB "
|
"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_type,
|
||||||
cdev->id.dev_model,
|
cdev->id.dev_model,
|
||||||
cdev->id.cu_type,
|
cdev->id.cu_type,
|
||||||
cdev->id.cu_model,
|
cdev->id.cu_model,
|
||||||
((private->rdc_data.blk_bdsa *
|
((private->rdc_data.blk_bdsa *
|
||||||
(private->rdc_data.blk_size >> 9)) >> 11),
|
(private->rdc_data.blk_size >> 9)) >> 11),
|
||||||
private->rdc_data.blk_size);
|
private->rdc_data.blk_size,
|
||||||
|
readonly ? ", read-only device" : "");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,8 @@ int dasd_gendisk_alloc(struct dasd_block *block)
|
||||||
}
|
}
|
||||||
len += sprintf(gdp->disk_name + len, "%c", 'a'+(base->devindex%26));
|
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);
|
set_disk_ro(gdp, 1);
|
||||||
gdp->private_data = block;
|
gdp->private_data = block;
|
||||||
gdp->queue = block->request_queue;
|
gdp->queue = block->request_queue;
|
||||||
|
|
|
@ -436,6 +436,10 @@ struct dasd_block {
|
||||||
#define DASD_FLAG_OFFLINE 3 /* device is in offline processing */
|
#define DASD_FLAG_OFFLINE 3 /* device is in offline processing */
|
||||||
#define DASD_FLAG_EER_SNSS 4 /* A SNSS is required */
|
#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_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 *);
|
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_set_stop_bits(struct dasd_device *, int);
|
||||||
void dasd_device_remove_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 */
|
/* externals in dasd_devmap.c */
|
||||||
extern int dasd_max_devindex;
|
extern int dasd_max_devindex;
|
||||||
extern int dasd_probeonly;
|
extern int dasd_probeonly;
|
||||||
|
|
|
@ -199,7 +199,8 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp)
|
||||||
if (!argp)
|
if (!argp)
|
||||||
return -EINVAL;
|
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;
|
return -EROFS;
|
||||||
if (copy_from_user(&fdata, argp, sizeof(struct format_data_t)))
|
if (copy_from_user(&fdata, argp, sizeof(struct format_data_t)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
@ -349,7 +350,8 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (get_user(intval, (int __user *)argp))
|
if (get_user(intval, (int __user *)argp))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
if (!intval && test_bit(DASD_FLAG_DEVICE_RO, &block->base->flags))
|
||||||
|
return -EROFS;
|
||||||
set_disk_ro(bdev->bd_disk, intval);
|
set_disk_ro(bdev->bd_disk, intval);
|
||||||
return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval);
|
return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue