Commit 8e09f215 authored by Stefan Weinhuber's avatar Stefan Weinhuber Committed by Martin Schwidefsky

[S390] dasd: add hyper PAV support to DASD device driver, part 1

Parallel access volumes (PAV) is a storage server feature, that allows
to start multiple channel programs on the same DASD in parallel. It
defines alias devices which can be used as alternative paths to the
same disk. With the old base PAV support we only needed rudimentary
functionality in the DASD device driver. As the mapping between base
and alias devices was static, we just had to export an identifier
(uid) and could leave the combining of devices to external layers
like a device mapper multipath.
Now hyper PAV removes the requirement to dedicate alias devices to
specific base devices. Instead each alias devices can be combined with
multiple base device on a per request basis. This requires full
support by the DASD device driver as now each channel program itself
has to identify the target base device.
The changes to the dasd device driver and the ECKD discipline are:
- Separate subchannel device representation (dasd_device) from block
  device representation (dasd_block). Only base devices are block
  devices.
- Gather information about base and alias devices and possible
  combinations.
- For each request decide which dasd_device should be used (base or
  alias) and build specific channel program.
- Support summary unit checks, which allow the storage server to
  upgrade / downgrade between base and hyper PAV at runtime (support
  is mandatory).
Signed-off-by: default avatarStefan Weinhuber <wein@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 0ac30be4
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
# S/390 block devices # S/390 block devices
# #
dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_9343_erp.o dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_alias.o
dasd_fba_mod-objs := dasd_fba.o dasd_3370_erp.o dasd_9336_erp.o dasd_fba_mod-objs := dasd_fba.o
dasd_diag_mod-objs := dasd_diag.o dasd_diag_mod-objs := dasd_diag.o
dasd_mod-objs := dasd.o dasd_ioctl.o dasd_proc.o dasd_devmap.o \ dasd_mod-objs := dasd.o dasd_ioctl.o dasd_proc.o dasd_devmap.o \
dasd_genhd.o dasd_erp.o dasd_genhd.o dasd_erp.o
......
This diff is collapsed.
/*
* File...........: linux/drivers/s390/block/dasd_3370_erp.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
*
*/
#define PRINTK_HEADER "dasd_erp(3370)"
#include "dasd_int.h"
/*
* DASD_3370_ERP_EXAMINE
*
* DESCRIPTION
* Checks only for fatal/no/recover error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
* The logic is based on the 'IBM 3880 Storage Control Reference' manual
* 'Chapter 7. 3370 Sense Data'.
*
* RETURN VALUES
* dasd_era_none no error
* dasd_era_fatal for all fatal (unrecoverable errors)
* dasd_era_recover for all others.
*/
dasd_era_t
dasd_3370_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
{
char *sense = irb->ecw;
/* check for successful execution first */
if (irb->scsw.cstat == 0x00 &&
irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
return dasd_era_none;
if (sense[0] & 0x80) { /* CMD reject */
return dasd_era_fatal;
}
if (sense[0] & 0x40) { /* Drive offline */
return dasd_era_recover;
}
if (sense[0] & 0x20) { /* Bus out parity */
return dasd_era_recover;
}
if (sense[0] & 0x10) { /* equipment check */
if (sense[1] & 0x80) {
return dasd_era_fatal;
}
return dasd_era_recover;
}
if (sense[0] & 0x08) { /* data check */
if (sense[1] & 0x80) {
return dasd_era_fatal;
}
return dasd_era_recover;
}
if (sense[0] & 0x04) { /* overrun */
if (sense[1] & 0x80) {
return dasd_era_fatal;
}
return dasd_era_recover;
}
if (sense[1] & 0x40) { /* invalid blocksize */
return dasd_era_fatal;
}
if (sense[1] & 0x04) { /* file protected */
return dasd_era_recover;
}
if (sense[1] & 0x01) { /* operation incomplete */
return dasd_era_recover;
}
if (sense[2] & 0x80) { /* check data erroor */
return dasd_era_recover;
}
if (sense[2] & 0x10) { /* Env. data present */
return dasd_era_recover;
}
/* examine the 24 byte sense data */
return dasd_era_recover;
} /* END dasd_3370_erp_examine */
This diff is collapsed.
/*
* File...........: linux/drivers/s390/block/dasd_9336_erp.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
*
*/
#define PRINTK_HEADER "dasd_erp(9336)"
#include "dasd_int.h"
/*
* DASD_9336_ERP_EXAMINE
*
* DESCRIPTION
* Checks only for fatal/no/recover error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
* The logic is based on the 'IBM 3880 Storage Control Reference' manual
* 'Chapter 7. 9336 Sense Data'.
*
* RETURN VALUES
* dasd_era_none no error
* dasd_era_fatal for all fatal (unrecoverable errors)
* dasd_era_recover for all others.
*/
dasd_era_t
dasd_9336_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
{
/* check for successful execution first */
if (irb->scsw.cstat == 0x00 &&
irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
return dasd_era_none;
/* examine the 24 byte sense data */
return dasd_era_recover;
} /* END dasd_9336_erp_examine */
/*
* File...........: linux/drivers/s390/block/dasd_9345_erp.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
*
*/
#define PRINTK_HEADER "dasd_erp(9343)"
#include "dasd_int.h"
dasd_era_t
dasd_9343_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
{
if (irb->scsw.cstat == 0x00 &&
irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
return dasd_era_none;
return dasd_era_recover;
}
This diff is collapsed.
...@@ -48,22 +48,6 @@ struct dasd_devmap { ...@@ -48,22 +48,6 @@ struct dasd_devmap {
struct dasd_uid uid; struct dasd_uid uid;
}; };
/*
* dasd_server_ssid_map contains a globally unique storage server subsystem ID.
* dasd_server_ssid_list contains the list of all subsystem IDs accessed by
* the DASD device driver.
*/
struct dasd_server_ssid_map {
struct list_head list;
struct system_id {
char vendor[4];
char serial[15];
__u16 ssid;
} sid;
};
static struct list_head dasd_server_ssid_list;
/* /*
* Parameter parsing functions for dasd= parameter. The syntax is: * Parameter parsing functions for dasd= parameter. The syntax is:
* <devno> : (0x)?[0-9a-fA-F]+ * <devno> : (0x)?[0-9a-fA-F]+
...@@ -721,8 +705,9 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr, ...@@ -721,8 +705,9 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
devmap->features &= ~DASD_FEATURE_READONLY; devmap->features &= ~DASD_FEATURE_READONLY;
if (devmap->device) if (devmap->device)
devmap->device->features = devmap->features; devmap->device->features = devmap->features;
if (devmap->device && devmap->device->gdp) if (devmap->device && devmap->device->block
set_disk_ro(devmap->device->gdp, val); && devmap->device->block->gdp)
set_disk_ro(devmap->device->block->gdp, val);
spin_unlock(&dasd_devmap_lock); spin_unlock(&dasd_devmap_lock);
return count; return count;
} }
...@@ -893,12 +878,16 @@ dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf) ...@@ -893,12 +878,16 @@ dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf)
devmap = dasd_find_busid(dev->bus_id); devmap = dasd_find_busid(dev->bus_id);
spin_lock(&dasd_devmap_lock); spin_lock(&dasd_devmap_lock);
if (!IS_ERR(devmap)) if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
alias = devmap->uid.alias; spin_unlock(&dasd_devmap_lock);
return sprintf(buf, "0\n");
}
if (devmap->uid.type == UA_BASE_PAV_ALIAS ||
devmap->uid.type == UA_HYPER_PAV_ALIAS)
alias = 1;
else else
alias = 0; alias = 0;
spin_unlock(&dasd_devmap_lock); spin_unlock(&dasd_devmap_lock);
return sprintf(buf, alias ? "1\n" : "0\n"); return sprintf(buf, alias ? "1\n" : "0\n");
} }
...@@ -930,19 +919,36 @@ static ssize_t ...@@ -930,19 +919,36 @@ static ssize_t
dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf) dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
{ {
struct dasd_devmap *devmap; struct dasd_devmap *devmap;
char uid[UID_STRLEN]; char uid_string[UID_STRLEN];
char ua_string[3];
struct dasd_uid *uid;
devmap = dasd_find_busid(dev->bus_id); devmap = dasd_find_busid(dev->bus_id);
spin_lock(&dasd_devmap_lock); spin_lock(&dasd_devmap_lock);
if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0) if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
snprintf(uid, sizeof(uid), "%s.%s.%04x.%02x",
devmap->uid.vendor, devmap->uid.serial,
devmap->uid.ssid, devmap->uid.unit_addr);
else
uid[0] = 0;
spin_unlock(&dasd_devmap_lock); spin_unlock(&dasd_devmap_lock);
return sprintf(buf, "\n");
return snprintf(buf, PAGE_SIZE, "%s\n", uid); }
uid = &devmap->uid;
switch (uid->type) {
case UA_BASE_DEVICE:
sprintf(ua_string, "%02x", uid->real_unit_addr);
break;
case UA_BASE_PAV_ALIAS:
sprintf(ua_string, "%02x", uid->base_unit_addr);
break;
case UA_HYPER_PAV_ALIAS:
sprintf(ua_string, "xx");
break;
default:
/* should not happen, treat like base device */
sprintf(ua_string, "%02x", uid->real_unit_addr);
break;
}
snprintf(uid_string, sizeof(uid_string), "%s.%s.%04x.%s",
uid->vendor, uid->serial, uid->ssid, ua_string);
spin_unlock(&dasd_devmap_lock);
return snprintf(buf, PAGE_SIZE, "%s\n", uid_string);
} }
static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL); static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL);
...@@ -1040,39 +1046,16 @@ int ...@@ -1040,39 +1046,16 @@ int
dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid) dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid)
{ {
struct dasd_devmap *devmap; struct dasd_devmap *devmap;
struct dasd_server_ssid_map *srv, *tmp;
devmap = dasd_find_busid(cdev->dev.bus_id); devmap = dasd_find_busid(cdev->dev.bus_id);
if (IS_ERR(devmap)) if (IS_ERR(devmap))
return PTR_ERR(devmap); return PTR_ERR(devmap);
/* generate entry for server_ssid_map */
srv = (struct dasd_server_ssid_map *)
kzalloc(sizeof(struct dasd_server_ssid_map), GFP_KERNEL);
if (!srv)
return -ENOMEM;
strncpy(srv->sid.vendor, uid->vendor, sizeof(srv->sid.vendor) - 1);
strncpy(srv->sid.serial, uid->serial, sizeof(srv->sid.serial) - 1);
srv->sid.ssid = uid->ssid;
/* server is already contained ? */
spin_lock(&dasd_devmap_lock); spin_lock(&dasd_devmap_lock);
devmap->uid = *uid; devmap->uid = *uid;
list_for_each_entry(tmp, &dasd_server_ssid_list, list) {
if (!memcmp(&srv->sid, &tmp->sid,
sizeof(struct system_id))) {
kfree(srv);
srv = NULL;
break;
}
}
/* add servermap to serverlist */
if (srv)
list_add(&srv->list, &dasd_server_ssid_list);
spin_unlock(&dasd_devmap_lock); spin_unlock(&dasd_devmap_lock);
return (srv ? 1 : 0); return 0;
} }
EXPORT_SYMBOL_GPL(dasd_set_uid); EXPORT_SYMBOL_GPL(dasd_set_uid);
...@@ -1138,9 +1121,6 @@ dasd_devmap_init(void) ...@@ -1138,9 +1121,6 @@ dasd_devmap_init(void)
dasd_max_devindex = 0; dasd_max_devindex = 0;
for (i = 0; i < 256; i++) for (i = 0; i < 256; i++)
INIT_LIST_HEAD(&dasd_hashlists[i]); INIT_LIST_HEAD(&dasd_hashlists[i]);
/* Initialize servermap structure. */
INIT_LIST_HEAD(&dasd_server_ssid_list);
return 0; return 0;
} }
......
...@@ -142,7 +142,7 @@ dasd_diag_erp(struct dasd_device *device) ...@@ -142,7 +142,7 @@ dasd_diag_erp(struct dasd_device *device)
int rc; int rc;
mdsk_term_io(device); mdsk_term_io(device);
rc = mdsk_init_io(device, device->bp_block, 0, NULL); rc = mdsk_init_io(device, device->block->bp_block, 0, NULL);
if (rc) if (rc)
DEV_MESSAGE(KERN_WARNING, device, "DIAG ERP unsuccessful, " DEV_MESSAGE(KERN_WARNING, device, "DIAG ERP unsuccessful, "
"rc=%d", rc); "rc=%d", rc);
...@@ -158,11 +158,11 @@ dasd_start_diag(struct dasd_ccw_req * cqr) ...@@ -158,11 +158,11 @@ dasd_start_diag(struct dasd_ccw_req * cqr)
struct dasd_diag_req *dreq; struct dasd_diag_req *dreq;
int rc; int rc;
device = cqr->device; device = cqr->startdev;
if (cqr->retries < 0) { if (cqr->retries < 0) {
DEV_MESSAGE(KERN_WARNING, device, "DIAG start_IO: request %p " DEV_MESSAGE(KERN_WARNING, device, "DIAG start_IO: request %p "
"- no retry left)", cqr); "- no retry left)", cqr);
cqr->status = DASD_CQR_FAILED; cqr->status = DASD_CQR_ERROR;
return -EIO; return -EIO;
} }
private = (struct dasd_diag_private *) device->private; private = (struct dasd_diag_private *) device->private;
...@@ -184,7 +184,7 @@ dasd_start_diag(struct dasd_ccw_req * cqr) ...@@ -184,7 +184,7 @@ dasd_start_diag(struct dasd_ccw_req * cqr)
switch (rc) { switch (rc) {
case 0: /* Synchronous I/O finished successfully */ case 0: /* Synchronous I/O finished successfully */
cqr->stopclk = get_clock(); cqr->stopclk = get_clock();
cqr->status = DASD_CQR_DONE; cqr->status = DASD_CQR_SUCCESS;
/* Indicate to calling function that only a dasd_schedule_bh() /* Indicate to calling function that only a dasd_schedule_bh()
and no timer is needed */ and no timer is needed */
rc = -EACCES; rc = -EACCES;
...@@ -209,12 +209,12 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr) ...@@ -209,12 +209,12 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr)
{ {
struct dasd_device *device; struct dasd_device *device;
device = cqr->device; device = cqr->startdev;
mdsk_term_io(device); mdsk_term_io(device);
mdsk_init_io(device, device->bp_block, 0, NULL); mdsk_init_io(device, device->block->bp_block, 0, NULL);
cqr->status = DASD_CQR_CLEAR; cqr->status = DASD_CQR_CLEAR_PENDING;
cqr->stopclk = get_clock(); cqr->stopclk = get_clock();
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
return 0; return 0;
} }
...@@ -247,7 +247,7 @@ dasd_ext_handler(__u16 code) ...@@ -247,7 +247,7 @@ dasd_ext_handler(__u16 code)
return; return;
} }
cqr = (struct dasd_ccw_req *) ip; cqr = (struct dasd_ccw_req *) ip;
device = (struct dasd_device *) cqr->device; device = (struct dasd_device *) cqr->startdev;
if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
DEV_MESSAGE(KERN_WARNING, device, DEV_MESSAGE(KERN_WARNING, device,
" magic number of dasd_ccw_req 0x%08X doesn't" " magic number of dasd_ccw_req 0x%08X doesn't"
...@@ -260,10 +260,10 @@ dasd_ext_handler(__u16 code) ...@@ -260,10 +260,10 @@ dasd_ext_handler(__u16 code)
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
/* Check for a pending clear operation */ /* Check for a pending clear operation */
if (cqr->status == DASD_CQR_CLEAR) { if (cqr->status == DASD_CQR_CLEAR_PENDING) {
cqr->status = DASD_CQR_QUEUED; cqr->status = DASD_CQR_CLEARED;
dasd_clear_timer(device); dasd_device_clear_timer(device);
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
return; return;
} }
...@@ -272,11 +272,11 @@ dasd_ext_handler(__u16 code) ...@@ -272,11 +272,11 @@ dasd_ext_handler(__u16 code)
expires = 0; expires = 0;
if (status == 0) { if (status == 0) {
cqr->status = DASD_CQR_DONE; cqr->status = DASD_CQR_SUCCESS;
/* Start first request on queue if possible -> fast_io. */ /* Start first request on queue if possible -> fast_io. */
if (!list_empty(&device->ccw_queue)) { if (!list_empty(&device->ccw_queue)) {
next = list_entry(device->ccw_queue.next, next = list_entry(device->ccw_queue.next,
struct dasd_ccw_req, list); struct dasd_ccw_req, devlist);
if (next->status == DASD_CQR_QUEUED) { if (next->status == DASD_CQR_QUEUED) {
rc = dasd_start_diag(next); rc = dasd_start_diag(next);
if (rc == 0) if (rc == 0)
...@@ -296,10 +296,10 @@ dasd_ext_handler(__u16 code) ...@@ -296,10 +296,10 @@ dasd_ext_handler(__u16 code)
} }
if (expires != 0) if (expires != 0)
dasd_set_timer(device, expires); dasd_device_set_timer(device, expires);
else else
dasd_clear_timer(device); dasd_device_clear_timer(device);
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
} }
...@@ -309,6 +309,7 @@ dasd_ext_handler(__u16 code) ...@@ -309,6 +309,7 @@ dasd_ext_handler(__u16 code)
static int static int
dasd_diag_check_device(struct dasd_device *device) dasd_diag_check_device(struct dasd_device *device)
{ {
struct dasd_block *block;
struct dasd_diag_private *private; struct dasd_diag_private *private;
struct dasd_diag_characteristics *rdc_data; struct dasd_diag_characteristics *rdc_data;
struct dasd_diag_bio bio; struct dasd_diag_bio bio;
...@@ -328,6 +329,16 @@ dasd_diag_check_device(struct dasd_device *device) ...@@ -328,6 +329,16 @@ dasd_diag_check_device(struct dasd_device *device)
ccw_device_get_id(device->cdev, &private->dev_id); ccw_device_get_id(device->cdev, &private->dev_id);
device->private = (void *) private; device->private = (void *) private;
} }
block = dasd_alloc_block();
if (IS_ERR(block)) {
DEV_MESSAGE(KERN_WARNING, device, "%s",
"could not allocate dasd block structure");
kfree(device->private);
return PTR_ERR(block);
}
device->block = block;
block->base = device;
/* Read Device Characteristics */ /* Read Device Characteristics */
rdc_data = (void *) &(private->rdc_data); rdc_data = (void *) &(private->rdc_data);
rdc_data->dev_nr = private->dev_id.devno; rdc_data->dev_nr = private->dev_id.devno;
...@@ -409,14 +420,14 @@ dasd_diag_check_device(struct dasd_device *device) ...@@ -409,14 +420,14 @@ dasd_diag_check_device(struct dasd_device *device)
sizeof(DASD_DIAG_CMS1)) == 0) { sizeof(DASD_DIAG_CMS1)) == 0) {
/* get formatted blocksize from label block */ /* get formatted blocksize from label block */
bsize = (unsigned int) label->block_size; bsize = (unsigned int) label->block_size;
device->blocks = (unsigned long) label->block_count; block->blocks = (unsigned long) label->block_count;
} else } else
device->blocks = end_block; block->blocks = end_block;
device->bp_block = bsize; block->bp_block = bsize;
device->s2b_shift = 0; /* bits to shift 512 to get a block */ block->s2b_shift = 0; /* bits to shift 512 to get a block */
for (sb = 512; sb < bsize; sb = sb << 1) for (sb = 512; sb < bsize; sb = sb << 1)
device->s2b_shift++; block->s2b_shift++;
rc = mdsk_init_io(device, device->bp_block, 0, NULL); rc = mdsk_init_io(device, block->bp_block, 0, NULL);
if (rc) { if (rc) {
DEV_MESSAGE(KERN_WARNING, device, "DIAG initialization " DEV_MESSAGE(KERN_WARNING, device, "DIAG initialization "
"failed (rc=%d)", rc); "failed (rc=%d)", rc);
...@@ -424,9 +435,9 @@ dasd_diag_check_device(struct dasd_device *device) ...@@ -424,9 +435,9 @@ dasd_diag_check_device(struct dasd_device *device)
} else { } else {
DEV_MESSAGE(KERN_INFO, device, DEV_MESSAGE(KERN_INFO, device,
"(%ld B/blk): %ldkB", "(%ld B/blk): %ldkB",
(unsigned long) device->bp_block, (unsigned long) block->bp_block,
(unsigned long) (device->blocks << (unsigned long) (block->blocks <<
device->s2b_shift) >> 1); block->s2b_shift) >> 1);
} }
out: out:
free_page((long) label); free_page((long) label);
...@@ -436,22 +447,16 @@ dasd_diag_check_device(struct dasd_device *device) ...@@ -436,22 +447,16 @@ dasd_diag_check_device(struct dasd_device *device)
/* Fill in virtual disk geometry for device. Return zero on success, non-zero /* Fill in virtual disk geometry for device. Return zero on success, non-zero
* otherwise. */ * otherwise. */
static int static int
dasd_diag_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) dasd_diag_fill_geometry(struct dasd_block *block, struct hd_geometry *geo)
{ {
if (dasd_check_blocksize(device->bp_block) != 0) if (dasd_check_blocksize(block->bp_block) != 0)
return -EINVAL; return -EINVAL;
geo->cylinders = (device->blocks << device->s2b_shift) >> 10; geo->cylinders = (block->blocks << block->s2b_shift) >> 10;
geo->heads = 16; geo->heads = 16;
geo->sectors = 128 >> device->s2b_shift; geo->sectors = 128 >> block->s2b_shift;
return 0; return 0;
} }
static dasd_era_t
dasd_diag_examine_error(struct dasd_ccw_req * cqr, struct irb * stat)
{
return dasd_era_fatal;
}
static dasd_erp_fn_t static dasd_erp_fn_t
dasd_diag_erp_action(struct dasd_ccw_req * cqr) dasd_diag_erp_action(struct dasd_ccw_req * cqr)
{ {
...@@ -466,8 +471,9 @@ dasd_diag_erp_postaction(struct dasd_ccw_req * cqr) ...@@ -466,8 +471,9 @@ dasd_diag_erp_postaction(struct dasd_ccw_req * cqr)
/* Create DASD request from block device request. Return pointer to new /* Create DASD request from block device request. Return pointer to new
* request on success, ERR_PTR otherwise. */ * request on success, ERR_PTR otherwise. */
static struct dasd_ccw_req * static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev,
dasd_diag_build_cp(struct dasd_device * device, struct request *req) struct dasd_block *block,
struct request *req)
{ {
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
struct dasd_diag_req *dreq; struct dasd_diag_req *dreq;
...@@ -486,17 +492,17 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) ...@@ -486,17 +492,17 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
rw_cmd = MDSK_WRITE_REQ; rw_cmd = MDSK_WRITE_REQ;
else else
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
blksize = device->bp_block; blksize = block->bp_block;
/* Calculate record id of first and last block. */ /* Calculate record id of first and last block. */
first_rec = req->sector >> device->s2b_shift; first_rec = req->sector >> block->s2b_shift;
last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift; last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift;
/* Check struct bio and count the number of blocks for the request. */ /* Check struct bio and count the number of blocks for the request. */
count = 0; count = 0;
rq_for_each_segment(bv, req, iter) { rq_for_each_segment(bv, req, iter) {
if (bv->bv_len & (blksize - 1)) if (bv->bv_len & (blksize - 1))
/* Fba can only do full blocks. */ /* Fba can only do full blocks. */
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
count += bv->bv_len >> (device->s2b_shift + 9); count += bv->bv_len >> (block->s2b_shift + 9);
} }
/* Paranoia. */ /* Paranoia. */
if (count != last_rec - first_rec + 1) if (count != last_rec - first_rec + 1)
...@@ -505,7 +511,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) ...@@ -505,7 +511,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
datasize = sizeof(struct dasd_diag_req) + datasize = sizeof(struct dasd_diag_req) +
count*sizeof(struct dasd_diag_bio); count*sizeof(struct dasd_diag_bio);
cqr = dasd_smalloc_request(dasd_diag_discipline.name, 0, cqr = dasd_smalloc_request(dasd_diag_discipline.name, 0,
datasize, device); datasize, memdev);
if (IS_ERR(cqr)) if (IS_ERR(cqr))
return cqr; return cqr;
...@@ -529,7 +535,9 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req) ...@@ -529,7 +535,9 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
cqr->buildclk = get_clock(); cqr->buildclk = get_clock();
if (req->cmd_flags & REQ_FAILFAST) if (req->cmd_flags & REQ_FAILFAST)
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
cqr->device = device; cqr->startdev = memdev;
cqr->memdev = memdev;
cqr->block = block;
cqr->expires = DIAG_TIMEOUT; cqr->expires = DIAG_TIMEOUT;
cqr->status = DASD_CQR_FILLED; cqr->status = DASD_CQR_FILLED;
return cqr; return cqr;
...@@ -543,10 +551,15 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) ...@@ -543,10 +551,15 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req)
int status; int status;
status = cqr->status == DASD_CQR_DONE; status = cqr->status == DASD_CQR_DONE;
dasd_sfree_request(cqr, cqr->device); dasd_sfree_request(cqr, cqr->memdev);
return status; return status;
} }
static void dasd_diag_handle_terminated_request(struct dasd_ccw_req *cqr)
{
cqr->status = DASD_CQR_FILLED;
};
/* Fill in IOCTL data for device. */ /* Fill in IOCTL data for device. */
static int static int
dasd_diag_fill_info(struct dasd_device * device, dasd_diag_fill_info(struct dasd_device * device,
...@@ -583,7 +596,7 @@ static struct dasd_discipline dasd_diag_discipline = { ...@@ -583,7 +596,7 @@ static struct dasd_discipline dasd_diag_discipline = {
.fill_geometry = dasd_diag_fill_geometry, .fill_geometry = dasd_diag_fill_geometry,
.start_IO = dasd_start_diag, .start_IO = dasd_start_diag,
.term_IO = dasd_diag_term_IO, .term_IO = dasd_diag_term_IO,
.examine_error = dasd_diag_examine_error, .handle_terminated_request = dasd_diag_handle_terminated_request,
.erp_action = dasd_diag_erp_action, .erp_action = dasd_diag_erp_action,
.erp_postaction = dasd_diag_erp_postaction, .erp_postaction = dasd_diag_erp_postaction,
.build_cp = dasd_diag_build_cp, .build_cp = dasd_diag_build_cp,
......
This diff is collapsed.
...@@ -39,6 +39,8 @@ ...@@ -39,6 +39,8 @@
#define DASD_ECKD_CCW_READ_CKD_MT 0x9e #define DASD_ECKD_CCW_READ_CKD_MT 0x9e
#define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d #define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d
#define DASD_ECKD_CCW_RESERVE 0xB4 #define DASD_ECKD_CCW_RESERVE 0xB4
#define DASD_ECKD_CCW_PFX 0xE7
#define DASD_ECKD_CCW_RSCK 0xF9
/* /*
* Perform Subsystem Function / Sub-Orders * Perform Subsystem Function / Sub-Orders
...@@ -137,6 +139,25 @@ struct LO_eckd_data { ...@@ -137,6 +139,25 @@ struct LO_eckd_data {
__u16 length; __u16 length;
} __attribute__ ((packed)); } __attribute__ ((packed));
/* Prefix data for format 0x00 and 0x01 */
struct PFX_eckd_data {
unsigned char format;
struct {
unsigned char define_extend:1;
unsigned char time_stamp:1;
unsigned char verify_base:1;
unsigned char hyper_pav:1;
unsigned char reserved:4;
} __attribute__ ((packed)) validity;
__u8 base_address;
__u8 aux;
__u8 base_lss;
__u8 reserved[7];
struct DE_eckd_data define_extend;
struct LO_eckd_data locate_record;
__u8 LO_extended_data[4];
} __attribute__ ((packed));
struct dasd_eckd_characteristics { struct dasd_eckd_characteristics {
__u16 cu_type; __u16 cu_type;
struct { struct {
...@@ -254,7 +275,9 @@ struct dasd_eckd_confdata { ...@@ -254,7 +275,9 @@ struct dasd_eckd_confdata {
} __attribute__ ((packed)) ned; } __attribute__ ((packed)) ned;
struct { struct {
unsigned char flags; /* byte 0 */ unsigned char flags; /* byte 0 */
unsigned char res2[7]; /* byte 1- 7 */ unsigned char res1; /* byte 1 */
__u16 format; /* byte 2-3 */
unsigned char res2[4]; /* byte 4-7 */
unsigned char sua_flags; /* byte 8 */ unsigned char sua_flags; /* byte 8 */
__u8 base_unit_addr; /* byte 9 */ __u8 base_unit_addr; /* byte 9 */
unsigned char res3[22]; /* byte 10-31 */ unsigned char res3[22]; /* byte 10-31 */
...@@ -343,6 +366,11 @@ struct dasd_eckd_path { ...@@ -343,6 +366,11 @@ struct dasd_eckd_path {
__u8 npm; __u8 npm;
}; };
struct dasd_rssd_features {
char feature[256];
} __attribute__((packed));
/* /*
* Perform Subsystem Function - Prepare for Read Subsystem Data * Perform Subsystem Function - Prepare for Read Subsystem Data
*/ */
...@@ -365,4 +393,99 @@ struct dasd_psf_ssc_data { ...@@ -365,4 +393,99 @@ struct dasd_psf_ssc_data {
unsigned char reserved[59]; unsigned char reserved[59];
} __attribute__((packed)); } __attribute__((packed));
/*
* some structures and definitions for alias handling
*/
struct dasd_unit_address_configuration {
struct {
char ua_type;
char base_ua;
} unit[256];
} __attribute__((packed));
#define MAX_DEVICES_PER_LCU 256
/* flags on the LCU */
#define NEED_UAC_UPDATE 0x01
#define UPDATE_PENDING 0x02
enum pavtype {NO_PAV, BASE_PAV, HYPER_PAV};
struct alias_root {
struct list_head serverlist;
spinlock_t lock;
};
struct alias_server {
struct list_head server;
struct dasd_uid uid;
struct list_head lculist;
};
struct summary_unit_check_work_data {
char reason;
struct dasd_device *device;
struct work_struct worker;
};
struct read_uac_work_data {
struct dasd_device *device;
struct delayed_work dwork;
};
struct alias_lcu {
struct list_head lcu;
struct dasd_uid uid;
enum pavtype pav;
char flags;
spinlock_t lock;
struct list_head grouplist;
struct list_head active_devices;
struct list_head inactive_devices;
struct dasd_unit_address_configuration *uac;
struct summary_unit_check_work_data suc_data;
struct read_uac_work_data ruac_data;
struct dasd_ccw_req *rsu_cqr;
};
struct alias_pav_group {
struct list_head group;
struct dasd_uid uid;
struct alias_lcu *lcu;
struct list_head baselist;
struct list_head aliaslist;
struct dasd_device *next;
};
struct dasd_eckd_private {
struct dasd_eckd_characteristics rdc_data;
struct dasd_eckd_confdata conf_data;
struct dasd_eckd_path path_data;
struct eckd_count count_area[5];
int init_cqr_status;
int uses_cdl;
struct attrib_data_t attrib; /* e.g. cache operations */
struct dasd_rssd_features features;
/* alias managemnet */
struct dasd_uid uid;
struct alias_pav_group *pavgroup;
struct alias_lcu *lcu;
int count;
};
int dasd_alias_make_device_known_to_lcu(struct dasd_device *);
void dasd_alias_disconnect_device_from_lcu(struct dasd_device *);
int dasd_alias_add_device(struct dasd_device *);
int dasd_alias_remove_device(struct dasd_device *);
struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *);
void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *);
void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *);
#endif /* DASD_ECKD_H */ #endif /* DASD_ECKD_H */
...@@ -336,7 +336,7 @@ static void dasd_eer_write_snss_trigger(struct dasd_device *device, ...@@ -336,7 +336,7 @@ static void dasd_eer_write_snss_trigger(struct dasd_device *device,
unsigned long flags; unsigned long flags;
struct eerbuffer *eerb; struct eerbuffer *eerb;
snss_rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; snss_rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
if (snss_rc) if (snss_rc)
data_size = 0; data_size = 0;
else else
...@@ -404,10 +404,11 @@ void dasd_eer_snss(struct dasd_device *device) ...@@ -404,10 +404,11 @@ void dasd_eer_snss(struct dasd_device *device)
set_bit(DASD_FLAG_EER_SNSS, &device->flags); set_bit(DASD_FLAG_EER_SNSS, &device->flags);
return; return;
} }
/* cdev is already locked, can't use dasd_add_request_head */
clear_bit(DASD_FLAG_EER_SNSS, &device->flags); clear_bit(DASD_FLAG_EER_SNSS, &device->flags);
cqr->status = DASD_CQR_QUEUED; cqr->status = DASD_CQR_QUEUED;
list_add(&cqr->list, &device->ccw_queue); list_add(&cqr->devlist, &device->ccw_queue);
dasd_schedule_bh(device); dasd_schedule_device_bh(device);
} }
/* /*
...@@ -415,7 +416,7 @@ void dasd_eer_snss(struct dasd_device *device) ...@@ -415,7 +416,7 @@ void dasd_eer_snss(struct dasd_device *device)
*/ */
static void dasd_eer_snss_cb(struct dasd_ccw_req *cqr, void *data) static void dasd_eer_snss_cb(struct dasd_ccw_req *cqr, void *data)
{ {
struct dasd_device *device = cqr->device; struct dasd_device *device = cqr->startdev;
unsigned long flags; unsigned long flags;
dasd_eer_write(device, cqr, DASD_EER_STATECHANGE); dasd_eer_write(device, cqr, DASD_EER_STATECHANGE);
...@@ -458,7 +459,7 @@ int dasd_eer_enable(struct dasd_device *device) ...@@ -458,7 +459,7 @@ int dasd_eer_enable(struct dasd_device *device)
if (!cqr) if (!cqr)
return -ENOMEM; return -ENOMEM;
cqr->device = device; cqr->startdev = device;
cqr->retries = 255; cqr->retries = 255;
cqr->expires = 10 * HZ; cqr->expires = 10 * HZ;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
......
...@@ -46,6 +46,8 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize, ...@@ -46,6 +46,8 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize,
if (cqr == NULL) if (cqr == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
memset(cqr, 0, sizeof(struct dasd_ccw_req)); memset(cqr, 0, sizeof(struct dasd_ccw_req));
INIT_LIST_HEAD(&cqr->devlist);
INIT_LIST_HEAD(&cqr->blocklist);
data = (char *) cqr + ((sizeof(struct dasd_ccw_req) + 7L) & -8L); data = (char *) cqr + ((sizeof(struct dasd_ccw_req) + 7L) & -8L);
cqr->cpaddr = NULL; cqr->cpaddr = NULL;
if (cplength > 0) { if (cplength > 0) {
...@@ -66,7 +68,7 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize, ...@@ -66,7 +68,7 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize,
} }
void void
dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device) dasd_free_erp_request(struct dasd_ccw_req *cqr, struct dasd_device * device)
{ {
unsigned long flags; unsigned long flags;
...@@ -81,11 +83,11 @@ dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device) ...@@ -81,11 +83,11 @@ dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
* dasd_default_erp_action just retries the current cqr * dasd_default_erp_action just retries the current cqr
*/ */
struct dasd_ccw_req * struct dasd_ccw_req *
dasd_default_erp_action(struct dasd_ccw_req * cqr) dasd_default_erp_action(struct dasd_ccw_req *cqr)
{ {
struct dasd_device *device; struct dasd_device *device;
device = cqr->device; device = cqr->startdev;
/* just retry - there is nothing to save ... I got no sense data.... */ /* just retry - there is nothing to save ... I got no sense data.... */
if (cqr->retries > 0) { if (cqr->retries > 0) {
...@@ -93,12 +95,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr) ...@@ -93,12 +95,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr)
"default ERP called (%i retries left)", "default ERP called (%i retries left)",
cqr->retries); cqr->retries);
cqr->lpm = LPM_ANYPATH; cqr->lpm = LPM_ANYPATH;
cqr->status = DASD_CQR_QUEUED; cqr->status = DASD_CQR_FILLED;
} else { } else {
DEV_MESSAGE (KERN_WARNING, device, "%s", DEV_MESSAGE (KERN_WARNING, device, "%s",
"default ERP called (NO retry left)"); "default ERP called (NO retry left)");
cqr->status = DASD_CQR_FAILED; cqr->status = DASD_CQR_FAILED;
cqr->stopclk = get_clock (); cqr->stopclk = get_clock();
} }
return cqr; return cqr;
} /* end dasd_default_erp_action */ } /* end dasd_default_erp_action */
...@@ -117,15 +119,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr) ...@@ -117,15 +119,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr)
* RETURN VALUES * RETURN VALUES
* cqr pointer to the original CQR * cqr pointer to the original CQR
*/ */
struct dasd_ccw_req * struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr)
dasd_default_erp_postaction(struct dasd_ccw_req * cqr)
{ {
struct dasd_device *device;
int success; int success;
BUG_ON(cqr->refers == NULL || cqr->function == NULL); BUG_ON(cqr->refers == NULL || cqr->function == NULL);
device = cqr->device;
success = cqr->status == DASD_CQR_DONE; success = cqr->status == DASD_CQR_DONE;
/* free all ERPs - but NOT the original cqr */ /* free all ERPs - but NOT the original cqr */
...@@ -133,10 +132,10 @@ dasd_default_erp_postaction(struct dasd_ccw_req * cqr) ...@@ -133,10 +132,10 @@ dasd_default_erp_postaction(struct dasd_ccw_req * cqr)
struct dasd_ccw_req *refers; struct dasd_ccw_req *refers;
refers = cqr->refers; refers = cqr->refers;
/* remove the request from the device queue */ /* remove the request from the block queue */
list_del(&cqr->list); list_del(&cqr->blocklist);
/* free the finished erp request */ /* free the finished erp request */
dasd_free_erp_request(cqr, device); dasd_free_erp_request(cqr, cqr->memdev);
cqr = refers; cqr = refers;
} }
...@@ -157,7 +156,7 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb) ...@@ -157,7 +156,7 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb)
{ {
struct dasd_device *device; struct dasd_device *device;
device = cqr->device; device = cqr->startdev;
/* dump sense data */ /* dump sense data */
if (device->discipline && device->discipline->dump_sense) if (device->discipline && device->discipline->dump_sense)
device->discipline->dump_sense(device, cqr, irb); device->discipline->dump_sense(device, cqr, irb);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -54,11 +54,16 @@ static int ...@@ -54,11 +54,16 @@ static int
dasd_devices_show(struct seq_file *m, void *v) dasd_devices_show(struct seq_file *m, void *v)
{ {
struct dasd_device *device; struct dasd_device *device;
struct dasd_block *block;
char *substr; char *substr;
device = dasd_device_from_devindex((unsigned long) v - 1); device = dasd_device_from_devindex((unsigned long) v - 1);
if (IS_ERR(device)) if (IS_ERR(device))
return 0; return 0;
if (device->block)
block = device->block;
else
return 0;
/* Print device number. */ /* Print device number. */
seq_printf(m, "%s", device->cdev->dev.bus_id); seq_printf(m, "%s", device->cdev->dev.bus_id);
/* Print discipline string. */ /* Print discipline string. */
...@@ -67,14 +72,14 @@ dasd_devices_show(struct seq_file *m, void *v) ...@@ -67,14 +72,14 @@ dasd_devices_show(struct seq_file *m, void *v)
else else
seq_printf(m, "(none)"); seq_printf(m, "(none)");
/* Print kdev. */ /* Print kdev. */
if (device->gdp) if (block->gdp)
seq_printf(m, " at (%3d:%6d)", seq_printf(m, " at (%3d:%6d)",
device->gdp->major, device->gdp->first_minor); block->gdp->major, block->gdp->first_minor);
else else
seq_printf(m, " at (???:??????)"); seq_printf(m, " at (???:??????)");
/* Print device name. */ /* Print device name. */
if (device->gdp) if (block->gdp)
seq_printf(m, " is %-8s", device->gdp->disk_name); seq_printf(m, " is %-8s", block->gdp->disk_name);
else else
seq_printf(m, " is ????????"); seq_printf(m, " is ????????");
/* Print devices features. */ /* Print devices features. */
...@@ -100,14 +105,14 @@ dasd_devices_show(struct seq_file *m, void *v) ...@@ -100,14 +105,14 @@ dasd_devices_show(struct seq_file *m, void *v)
case DASD_STATE_READY: case DASD_STATE_READY:
case DASD_STATE_ONLINE: case DASD_STATE_ONLINE:
seq_printf(m, "active "); seq_printf(m, "active ");
if (dasd_check_blocksize(device->bp_block)) if (dasd_check_blocksize(block->bp_block))
seq_printf(m, "n/f "); seq_printf(m, "n/f ");
else else
seq_printf(m, seq_printf(m,
"at blocksize: %d, %ld blocks, %ld MB", "at blocksize: %d, %ld blocks, %ld MB",
device->bp_block, device->blocks, block->bp_block, block->blocks,
((device->bp_block >> 9) * ((block->bp_block >> 9) *
device->blocks) >> 11); block->blocks) >> 11);
break; break;
default: default:
seq_printf(m, "no stat"); seq_printf(m, "no stat");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment