From: Martin Schwidefsky <schwidefsky@de.ibm.com>

 - Fix interrupt status examination.
 - Make dasd device attributes dependent on the devmap structure instead of
   the device structure to make them persistent and to be able to modify
   them in the offline state.
 - Allow changing the readonly attribute while dasd is online.
 - Add (diag) option to dasd= paramter.
 - Add missing spin_lock_init call.
 - Increase ref_count in dasd_device_from_cdev and add matching
   dasd_put_device pairs.
 - Adapt to notify api change in cio.
 - Fix bug in 3990 error recovery for cable pulls on ESS.
 - Replace kmap by page_address (no highmem on s/390).
 - Set correct default cache mode on ESS for eckd devices.
 - Change dasd names from "dasdx" to "dasd_<busid>_".



---

 25-akpm/drivers/s390/block/dasd.c          |  255 +++++++++++------------------
 25-akpm/drivers/s390/block/dasd_3990_erp.c |    8 
 25-akpm/drivers/s390/block/dasd_devmap.c   |  233 +++++++++++++++++++++++---
 25-akpm/drivers/s390/block/dasd_diag.c     |    4 
 25-akpm/drivers/s390/block/dasd_eckd.c     |   43 ++++
 25-akpm/drivers/s390/block/dasd_fba.c      |    7 
 25-akpm/drivers/s390/block/dasd_genhd.c    |   21 --
 25-akpm/drivers/s390/block/dasd_int.h      |   11 -
 25-akpm/drivers/s390/block/dasd_proc.c     |   11 -
 25-akpm/include/asm-s390/dasd.h            |    6 
 25-akpm/include/linux/genhd.h              |    2 
 11 files changed, 378 insertions(+), 223 deletions(-)

diff -puN drivers/s390/block/dasd_3990_erp.c~s390-04-dasd-driver drivers/s390/block/dasd_3990_erp.c
--- 25/drivers/s390/block/dasd_3990_erp.c~s390-04-dasd-driver	Thu Jan  8 14:11:30 2004
+++ 25-akpm/drivers/s390/block/dasd_3990_erp.c	Thu Jan  8 14:11:30 2004
@@ -5,7 +5,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001
  *
- * $Revision: 1.25 $
+ * $Revision: 1.26 $
  */
 
 #include <linux/timer.h>
@@ -312,7 +312,8 @@ dasd_3990_erp_alternate_path(struct dasd
 			    erp->lpm, erp->dstat->esw.esw0.sublog.lpum, opm);
 
 		/* reset status to queued to handle the request again... */
-		erp->status = DASD_CQR_QUEUED;
+		if (erp->status > DASD_CQR_QUEUED)
+			erp->status = DASD_CQR_QUEUED;
 		erp->retries = 1;
 	} else {
 		DEV_MESSAGE(KERN_ERR, device,
@@ -321,7 +322,8 @@ dasd_3990_erp_alternate_path(struct dasd
 			    erp->dstat->esw.esw0.sublog.lpum, opm);
 
 		/* post request with permanent error */
-		erp->status = DASD_CQR_FAILED;
+		if (erp->status > DASD_CQR_QUEUED)
+			erp->status = DASD_CQR_FAILED;
 	}
 }				/* end dasd_3990_erp_alternate_path */
 
diff -puN drivers/s390/block/dasd.c~s390-04-dasd-driver drivers/s390/block/dasd.c
--- 25/drivers/s390/block/dasd.c~s390-04-dasd-driver	Thu Jan  8 14:11:30 2004
+++ 25-akpm/drivers/s390/block/dasd.c	Thu Jan  8 14:11:30 2004
@@ -7,7 +7,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
  *
- * $Revision: 1.114 $
+ * $Revision: 1.123 $
  */
 
 #include <linux/config.h>
@@ -43,7 +43,6 @@ MODULE_DESCRIPTION("Linux on S/390 DASD 
 		   " Copyright 2000 IBM Corporation");
 MODULE_SUPPORTED_DEVICE("dasd");
 MODULE_PARM(dasd, "1-" __MODULE_STRING(256) "s");
-MODULE_PARM(dasd_disciplines, "1-" __MODULE_STRING(8) "s");
 MODULE_LICENSE("GPL");
 
 /*
@@ -57,7 +56,6 @@ static void dasd_int_handler(struct ccw_
 static void dasd_flush_ccw_queue(struct dasd_device *, int);
 static void dasd_tasklet(struct dasd_device *);
 static void do_kick_device(void *data);
-static int  dasd_add_sysfs_files(struct ccw_device *cdev);
 
 /*
  * SECTION: Operations on the device structure.
@@ -93,6 +91,7 @@ dasd_alloc_device(void)
 
 	dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2);
 	dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE);
+	spin_lock_init(&device->mem_lock);
 	spin_lock_init(&device->request_queue_lock);
 	atomic_set (&device->tasklet_scheduled, 0);
 	tasklet_init(&device->tasklet, 
@@ -860,7 +859,7 @@ dasd_handle_killed_request(struct ccw_de
 
 	device = (struct dasd_device *) cqr->device;
 	if (device == NULL ||
-	    device != cdev->dev.driver_data ||
+	    device != dasd_device_from_cdev(cdev) ||
 	    strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
 		MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
 			cdev->dev.bus_id);
@@ -872,6 +871,7 @@ dasd_handle_killed_request(struct ccw_de
 
 	dasd_clear_timer(device);
 	dasd_schedule_bh(device);
+	dasd_put_device(device);
 }
 
 static void
@@ -931,7 +931,11 @@ dasd_int_handler(struct ccw_device *cdev
 	/* first of all check for state change pending interrupt */
 	mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
 	if ((irb->scsw.dstat & mask) == mask) {
-		dasd_handle_state_change_pending(cdev->dev.driver_data);
+		device = dasd_device_from_cdev(cdev);
+		if (!IS_ERR(device)) {
+			dasd_handle_state_change_pending(device);
+			dasd_put_device(device);
+		}
 		return;
 	}
 
@@ -949,7 +953,6 @@ dasd_int_handler(struct ccw_device *cdev
 
 	device = (struct dasd_device *) cqr->device;
 	if (device == NULL ||
-	    device != cdev->dev.driver_data ||
 	    strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
 		MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
 			cdev->dev.bus_id);
@@ -959,17 +962,19 @@ dasd_int_handler(struct ccw_device *cdev
 	DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x",
 		      ((irb->scsw.cstat << 8) | irb->scsw.dstat));
 
-	/* Find out the appropriate era_action. */
-	era = dasd_era_none;
-	if (irb->scsw.dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END) ||
-	    irb->esw.esw0.erw.cons) {
-		/* The request did end abnormally. */
-		if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC)
-			era = dasd_era_fatal;
-		else
-			era = device->discipline->examine_error(cqr, irb);
-		DBF_EVENT(DBF_NOTICE, "era_code %d", era);
-	}
+ 	/* Find out the appropriate era_action. */
+	if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) 
+		era = dasd_era_fatal;
+	else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
+		 irb->scsw.cstat == 0 &&
+		 !irb->esw.esw0.erw.cons)
+		era = dasd_era_none;
+	else if (irb->esw.esw0.erw.cons)
+		era = device->discipline->examine_error(cqr, irb);
+	else 
+		era = dasd_era_recover;
+
+	DBF_DEV_EVENT(DBF_DEBUG, device, "era_code %d", era);
 	expires = 0;
 	if (era == dasd_era_none) {
 		cqr->status = DASD_CQR_DONE;
@@ -1149,6 +1154,11 @@ __dasd_process_blk_queue(struct dasd_dev
 			dasd_end_request(req, 0);
 			continue;
 		}
+		if (device->stopped & DASD_STOPPED_DC_EIO) {
+			blkdev_dequeue_request(req);
+			dasd_end_request(req, 0);
+			continue;
+		}
 		cqr = device->discipline->build_cp(device, req);
 		if (IS_ERR(cqr)) {
 			if (PTR_ERR(cqr) == -ENOMEM)
@@ -1728,17 +1738,10 @@ int
 dasd_generic_probe (struct ccw_device *cdev,
 		    struct dasd_discipline *discipline)
 {
-	int ret = 0;
-
-	if (dasd_autodetect &&
-	    (ret = dasd_add_busid(cdev->dev.bus_id, DASD_FEATURE_DEFAULT))) {
-		printk (KERN_WARNING
-			"dasd_generic_probe: cannot autodetect %s\n",
-			cdev->dev.bus_id);
-		return ret;
-	}
+	int ret;
 
-	if (!ret && (ret = dasd_add_sysfs_files(cdev))) {
+	ret = dasd_add_sysfs_files(cdev);
+	if (ret) {
 		printk(KERN_WARNING
 		       "dasd_generic_probe: could not add driverfs entries"
 		       "for %s\n", cdev->dev.bus_id);
@@ -1751,16 +1754,17 @@ dasd_generic_probe (struct ccw_device *c
 
 /* this will one day be called from a global not_oper handler.
  * It is also used by driver_unregister during module unload */
-int
+void
 dasd_generic_remove (struct ccw_device *cdev)
 {
 	struct dasd_device *device;
 
-	device = cdev->dev.driver_data;
-	cdev->dev.driver_data = NULL;
-	if (device)
-		kfree(device);
-	return 0;
+	device = dasd_device_from_cdev(cdev);
+	if (!IS_ERR(device)) {
+		dasd_set_target_state(device, DASD_STATE_NEW);
+		/* dasd_delete_device destroys the device reference. */
+		dasd_delete_device(device);
+	}
 }
 
 /* activate a device. This is called from dasd_{eckd,fba}_probe() when either
@@ -1779,24 +1783,14 @@ dasd_generic_set_online (struct ccw_devi
 		return PTR_ERR(device);
 
 	if (device->use_diag_flag)
-		device->discipline = dasd_diag_discipline_pointer;
-
-	rc = 0;
-	if (!device->discipline ||
-	    (rc = device->discipline->check_device(device))) {
-		pr_debug("device %s is not diag (%d)\n", 
-			 cdev->dev.bus_id, rc);
-		if (device->private != NULL) {
-			kfree(device->private);
-			device->private = NULL;
-		}
-		device->discipline = discipline;
-		rc = discipline->check_device(device);
-	}
-
+		discipline = dasd_diag_discipline_pointer;
+	device->discipline = discipline;
+	rc = discipline->check_device(device);
 	if (rc) {
-		printk (KERN_WARNING "dasd_generic found a bad device %s\n", 
-			cdev->dev.bus_id);
+		printk (KERN_WARNING
+			"dasd_generic couldn't online device %s "
+			"with discipline %s\n", 
+			cdev->dev.bus_id, discipline->name);
 		dasd_delete_device(device);
 		return rc;
 	}
@@ -1809,16 +1803,16 @@ dasd_generic_set_online (struct ccw_devi
 		rc = -ENODEV;
 		dasd_set_target_state(device, DASD_STATE_NEW);
 		dasd_delete_device(device);
-	} else {
+	} else
 		pr_debug("dasd_generic device %s found\n",
 				cdev->dev.bus_id);
-		cdev->dev.driver_data = device;
-	}
 
 	/* FIXME: we have to wait for the root device but we don't want
 	 * to wait for each single device but for all at once. */
 	wait_event(dasd_init_waitq, _wait_for_device(device));
 
+	dasd_put_device(device);
+
 	return rc;
 }
 
@@ -1827,18 +1821,68 @@ dasd_generic_set_offline (struct ccw_dev
 {
 	struct dasd_device *device;
 
-	device = cdev->dev.driver_data;
-	dasd_set_target_state(device, DASD_STATE_NEW);
-	dasd_delete_device(device);
-	
+	device = dasd_device_from_cdev(cdev);
+	if (atomic_read(&device->open_count) > 0) {
+		printk (KERN_WARNING "Can't offline dasd device with open"
+			" count = %i.\n",
+			atomic_read(&device->open_count));
+		dasd_put_device(device);
+		return -EBUSY;
+	}
+	dasd_put_device(device);
+	dasd_generic_remove (cdev);
 	return 0;
 }
 
+int
+dasd_generic_notify(struct ccw_device *cdev, int event)
+{
+	struct dasd_device *device;
+	struct dasd_ccw_req *cqr;
+	unsigned long flags;
+	int ret;
+
+	device = dasd_device_from_cdev(cdev);
+	if (IS_ERR(device))
+		return 0;
+	spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+	ret = 0;
+	switch (event) {
+	case CIO_GONE:
+	case CIO_NO_PATH:
+		if (device->state < DASD_STATE_BASIC)
+			break;
+		/* Device is active. We want to keep it. */
+		if (device->disconnect_error_flag) {
+			list_for_each_entry(cqr, &device->ccw_queue, list)
+				if (cqr->status == DASD_CQR_IN_IO)
+					cqr->status = DASD_CQR_FAILED;
+			device->stopped |= DASD_STOPPED_DC_EIO;
+			dasd_schedule_bh(device);
+		} else {
+			list_for_each_entry(cqr, &device->ccw_queue, list)
+				if (cqr->status == DASD_CQR_IN_IO)
+					cqr->status = DASD_CQR_QUEUED;
+			device->stopped |= DASD_STOPPED_DC_WAIT;
+			dasd_set_timer(device, 0);
+		}
+		ret = 1;
+		break;
+	case CIO_OPER:
+		/* FIXME: add a sanity check. */
+		device->stopped &= ~(DASD_STOPPED_DC_WAIT|DASD_STOPPED_DC_EIO);
+		dasd_schedule_bh(device);
+		ret = 1;
+		break;
+	}
+	spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+	dasd_put_device(device);
+	return ret;
+}
+
 /*
  * Automatically online either all dasd devices (dasd_autodetect) or
- * all devices specified with dasd= parameters. For dasd_autodetect
- * dasd_generic_probe has added devmaps for all dasd devices. We
- * scan all present dasd devmaps and call ccw_device_set_online.
+ * all devices specified with dasd= parameters.
  */
 void
 dasd_generic_auto_online (struct ccw_driver *dasd_discipline_driver)
@@ -1863,97 +1907,6 @@ dasd_generic_auto_online (struct ccw_dri
 	put_driver(drv);
 }
 
-/*
- * SECTION: files in sysfs
- */
-
-/*
- * readonly controls the readonly status of a dasd
- */
-static ssize_t
-dasd_ro_show(struct device *dev, char *buf)
-{
-	struct dasd_device *device;
-
-	device = dev->driver_data;
-	if (!device)
-		return snprintf(buf, PAGE_SIZE, "n/a\n");
-
-	return snprintf(buf, PAGE_SIZE, device->ro_flag ? "1\n" : "0\n");
-}
-
-static ssize_t
-dasd_ro_store(struct device *dev, const char *buf, size_t count)
-{
-	struct dasd_device *device = dev->driver_data;
-
-	if (device)
-		device->ro_flag = (buf[0] == '1') ? 1 : 0;
-	return count;
-}
-
-static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store);
-
-/*
- * use_diag controls whether the driver should use diag rather than ssch
- * to talk to the device
- */
-/* TODO: Implement */
-static ssize_t 
-dasd_use_diag_show(struct device *dev, char *buf)
-{
-	struct dasd_device *device;
-
-	device = dev->driver_data;
-	if (!device)
-		return sprintf(buf, "n/a\n");
-
-	return sprintf(buf, device->use_diag_flag ? "1\n" : "0\n");
-}
-
-static ssize_t
-dasd_use_diag_store(struct device *dev, const char *buf, size_t count)
-{
-	struct dasd_device *device = dev->driver_data;
-
-	if (device)
-		device->use_diag_flag = (buf[0] == '1') ? 1 : 0;
-	return count;
-}
-
-static
-DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store);
-
-static ssize_t
-dasd_discipline_show(struct device *dev, char *buf)
-{
-	struct dasd_device *device;
-
-	device = dev->driver_data;
-	if (!device || !device->discipline)
-		return sprintf(buf, "none\n");
-	return snprintf(buf, PAGE_SIZE, "%s\n", device->discipline->name);
-}
-
-static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
-
-static struct attribute * dasd_attrs[] = {
-	&dev_attr_readonly.attr,
-	&dev_attr_discipline.attr,
-	&dev_attr_use_diag.attr,
-	NULL,
-};
-
-static struct attribute_group dasd_attr_group = {
-	.attrs = dasd_attrs,
-};
-
-static int
-dasd_add_sysfs_files(struct ccw_device *cdev)
-{
-	return sysfs_create_group(&cdev->dev.kobj, &dasd_attr_group);
-}
-
 static int __init
 dasd_init(void)
 {
diff -puN drivers/s390/block/dasd_devmap.c~s390-04-dasd-driver drivers/s390/block/dasd_devmap.c
--- 25/drivers/s390/block/dasd_devmap.c~s390-04-dasd-driver	Thu Jan  8 14:11:30 2004
+++ 25-akpm/drivers/s390/block/dasd_devmap.c	Thu Jan  8 14:11:30 2004
@@ -11,7 +11,7 @@
  * functions may not be called from interrupt context. In particular
  * dasd_get_device is a no-no from interrupt context.
  *
- * $Revision: 1.19 $
+ * $Revision: 1.25 $
  */
 
 #include <linux/config.h>
@@ -79,6 +79,8 @@ static spinlock_t dasd_devmap_lock = SPI
 static struct list_head dasd_hashlists[256];
 int dasd_max_devindex;
 
+static struct dasd_devmap *dasd_add_busid(char *, int);
+
 static inline int
 dasd_hash_busid(char *bus_id)
 {
@@ -168,12 +170,16 @@ dasd_feature_list(char *str, char **endp
 		*endp = str;
 		return DASD_FEATURE_DEFAULT;
 	}
+	str++;
 	features = 0;
+
 	while (1) {
 		for (len = 0; 
 		     str[len] && str[len] != ':' && str[len] != ')'; len++);
 		if (len == 2 && !strncmp(str, "ro", 2))
 			features |= DASD_FEATURE_READONLY;
+		else if (len == 4 && !strncmp(str, "diag", 4))
+			features |= DASD_FEATURE_USEDIAG;
 		else {
 			MESSAGE(KERN_WARNING,
 				"unsupported feature: %*s, "
@@ -203,6 +209,7 @@ dasd_feature_list(char *str, char **endp
 static inline int
 dasd_ranges_list(char *str)
 {
+	struct dasd_devmap *devmap;
 	int from, from_id0, from_id1;
 	int to, to_id0, to_id1;
 	int features, rc;
@@ -233,9 +240,9 @@ dasd_ranges_list(char *str)
 		while (from <= to) {
 			sprintf(bus_id, "%01x.%01x.%04x",
 				from_id0, from_id1, from++);
-			rc = dasd_add_busid(bus_id, features);
-			if (rc)
-				return rc;
+			devmap = dasd_add_busid(bus_id, features);
+			if (IS_ERR(devmap))
+				return PTR_ERR(devmap);
 		}
 		if (*str != ',')
 			break;
@@ -299,7 +306,7 @@ dasd_parse(void)
  * added through this function will define the kdevs for the individual
  * devices. 
  */
-int
+static struct dasd_devmap *
 dasd_add_busid(char *bus_id, int features)
 {
 	struct dasd_devmap *devmap, *new, *tmp;
@@ -308,7 +315,7 @@ dasd_add_busid(char *bus_id, int feature
 	new = (struct dasd_devmap *)
 		kmalloc(sizeof(struct dasd_devmap), GFP_KERNEL);
 	if (!new)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 	spin_lock(&dasd_devmap_lock);
 	devmap = 0;
 	hash = dasd_hash_busid(bus_id);
@@ -330,7 +337,7 @@ dasd_add_busid(char *bus_id, int feature
 	spin_unlock(&dasd_devmap_lock);
 	if (new)
 		kfree(new);
-	return 0;
+	return devmap;
 }
 
 /*
@@ -343,7 +350,7 @@ dasd_find_busid(char *bus_id)
 	int hash;
 
 	spin_lock(&dasd_devmap_lock);
-	devmap = 0;
+	devmap = ERR_PTR(-ENODEV);
 	hash = dasd_hash_busid(bus_id);
 	list_for_each_entry(tmp, &dasd_hashlists[hash], list) {
 		if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) {
@@ -361,7 +368,7 @@ dasd_find_busid(char *bus_id)
 int
 dasd_busid_known(char *bus_id)
 {
-	return dasd_find_busid(bus_id) ? 0 : -ENOENT;
+	return IS_ERR(dasd_find_busid(bus_id)) ? -ENOENT : 0;
 }
 
 /*
@@ -415,6 +422,28 @@ dasd_device_from_devindex(int devindex)
 }
 
 /*
+ * Return devmap for cdev. If no devmap exists yet, create one and
+ * connect it to the cdev.
+ */
+static struct dasd_devmap *
+dasd_devmap_from_cdev(struct ccw_device *cdev)
+{
+	struct dasd_devmap *devmap;
+
+	if (cdev->dev.driver_data)
+		return (struct dasd_devmap *) cdev->dev.driver_data;
+	devmap = dasd_find_busid(cdev->dev.bus_id);
+	if (!IS_ERR(devmap)) {
+		cdev->dev.driver_data = devmap;
+		return devmap;
+	}
+	devmap = dasd_add_busid(cdev->dev.bus_id, DASD_FEATURE_DEFAULT);
+	if (!IS_ERR(devmap))
+		cdev->dev.driver_data = devmap;
+	return devmap;
+}
+
+/*
  * Create a dasd device structure for cdev.
  */
 struct dasd_device *
@@ -424,41 +453,35 @@ dasd_create_device(struct ccw_device *cd
 	struct dasd_device *device;
 	int rc;
 
-	rc = dasd_add_busid(cdev->dev.bus_id, DASD_FEATURE_DEFAULT);
-	if (rc)
-		return ERR_PTR(rc);
-
-	devmap = dasd_find_busid(cdev->dev.bus_id);
+	devmap = dasd_devmap_from_cdev(cdev);
 	if (IS_ERR(devmap))
 		return (void *) devmap;
 
 	device = dasd_alloc_device();
 	if (IS_ERR(device))
 		return device;
-	atomic_set(&device->ref_count, 1);
-	device->devindex = devmap->devindex;
-	device->ro_flag = (devmap->features & DASD_FEATURE_READONLY) ? 1 : 0;
-	device->use_diag_flag = 1;
+	atomic_set(&device->ref_count, 2);
 
-	spin_lock_irq(get_ccwdev_lock(cdev));
-	if (cdev->dev.driver_data == NULL) {
+	spin_lock(&dasd_devmap_lock);
+	if (!devmap->device) {
+		devmap->device = device;
+		device->devindex = devmap->devindex;
+		device->ro_flag = 
+			(devmap->features & DASD_FEATURE_READONLY) != 0;
+		device->use_diag_flag = 
+			(devmap->features & DASD_FEATURE_USEDIAG) != 0;
 		get_device(&cdev->dev);
-		cdev->dev.driver_data = device;
 		device->cdev = cdev;
 		rc = 0;
 	} else
 		/* Someone else was faster. */
 		rc = -EBUSY;
-	spin_unlock_irq(get_ccwdev_lock(cdev));
+	spin_unlock(&dasd_devmap_lock);
+
 	if (rc) {
 		dasd_free_device(device);
 		return ERR_PTR(rc);
 	}
-	/* Device created successfully. Make it known via devmap. */
-	spin_lock(&dasd_devmap_lock);
-	devmap->device = device;
-	spin_unlock(&dasd_devmap_lock);
-
 	return device;
 }
 
@@ -468,7 +491,8 @@ dasd_create_device(struct ccw_device *cd
 static DECLARE_WAIT_QUEUE_HEAD(dasd_delete_wq);
 
 /*
- * Remove a dasd device structure.
+ * Remove a dasd device structure. The passed referenced
+ * is destroyed.
  */
 void
 dasd_delete_device(struct dasd_device *device)
@@ -479,17 +503,24 @@ dasd_delete_device(struct dasd_device *d
 	/* First remove device pointer from devmap. */
 	devmap = dasd_find_busid(device->cdev->dev.bus_id);
 	spin_lock(&dasd_devmap_lock);
+	if (devmap->device != device) {
+		spin_unlock(&dasd_devmap_lock);
+		dasd_put_device(device);
+		return;
+	}
 	devmap->device = NULL;
 	spin_unlock(&dasd_devmap_lock);
 
+	/* Drop ref_count by 2, one for the devmap reference and
+	 * one for the passed reference. */
+	atomic_sub(2, &device->ref_count);
+
 	/* Wait for reference counter to drop to zero. */
-	atomic_dec(&device->ref_count);
 	wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0);
 
 	/* Disconnect dasd_device structure from ccw_device structure. */
 	cdev = device->cdev;
 	device->cdev = NULL;
-	cdev->dev.driver_data = NULL;
 
 	/* Put ccw_device structure. */
 	put_device(&cdev->dev);
@@ -508,6 +539,148 @@ dasd_put_device_wake(struct dasd_device 
 	wake_up(&dasd_delete_wq);
 }
 
+/*
+ * Return dasd_device structure associated with cdev.
+ */
+struct dasd_device *
+dasd_device_from_cdev(struct ccw_device *cdev)
+{
+	struct dasd_devmap *devmap;
+	struct dasd_device *device;
+
+	device = ERR_PTR(-ENODEV);
+	spin_lock(&dasd_devmap_lock);
+	devmap = cdev->dev.driver_data;
+	if (devmap && devmap->device) {
+		device = devmap->device;
+		dasd_get_device(device);
+	}
+	spin_unlock(&dasd_devmap_lock);
+	return device;
+}
+
+/*
+ * SECTION: files in sysfs
+ */
+
+/*
+ * readonly controls the readonly status of a dasd
+ */
+static ssize_t
+dasd_ro_show(struct device *dev, char *buf)
+{
+	struct dasd_devmap *devmap;
+	int ro_flag;
+
+	devmap = dev->driver_data;
+	if (devmap)
+		ro_flag = (devmap->features & DASD_FEATURE_READONLY) != 0;
+	else
+		ro_flag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_READONLY) != 0;
+	return snprintf(buf, PAGE_SIZE, ro_flag ? "1\n" : "0\n");
+}
+
+static ssize_t
+dasd_ro_store(struct device *dev, const char *buf, size_t count)
+{
+	struct dasd_devmap *devmap;
+	int ro_flag;
+
+	devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
+	ro_flag = buf[0] == '1';
+	spin_lock(&dasd_devmap_lock);
+	if (ro_flag)
+		devmap->features |= DASD_FEATURE_READONLY;
+	else
+		devmap->features &= ~DASD_FEATURE_READONLY;
+	if (devmap->device) {
+		if (devmap->device->gdp)
+			set_disk_ro(devmap->device->gdp, ro_flag);
+		devmap->device->ro_flag = ro_flag;
+	}
+	spin_unlock(&dasd_devmap_lock);
+	return count;
+}
+
+static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store);
+
+/*
+ * use_diag controls whether the driver should use diag rather than ssch
+ * to talk to the device
+ */
+/* TODO: Implement */
+static ssize_t 
+dasd_use_diag_show(struct device *dev, char *buf)
+{
+	struct dasd_devmap *devmap;
+	int use_diag;
+
+	devmap = dev->driver_data;
+	if (devmap)
+		use_diag = (devmap->features & DASD_FEATURE_USEDIAG) != 0;
+	else
+		use_diag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_USEDIAG) != 0;
+	return sprintf(buf, use_diag ? "1\n" : "0\n");
+}
+
+static ssize_t
+dasd_use_diag_store(struct device *dev, const char *buf, size_t count)
+{
+	struct dasd_devmap *devmap;
+	int use_diag;
+
+	devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
+	use_diag = buf[0] == '1';
+	spin_lock(&dasd_devmap_lock);
+	/* Changing diag discipline flag is only allowed in offline state. */
+	if (!devmap->device) {
+		if (use_diag)
+			devmap->features |= DASD_FEATURE_USEDIAG;
+		else
+			devmap->features &= ~DASD_FEATURE_USEDIAG;
+	} else
+		count = -EPERM;
+	spin_unlock(&dasd_devmap_lock);
+	return count;
+}
+
+static
+DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store);
+
+static ssize_t
+dasd_discipline_show(struct device *dev, char *buf)
+{
+	struct dasd_devmap *devmap;
+	char *dname;
+
+	spin_lock(&dasd_devmap_lock);
+	dname = "none";
+	devmap = dev->driver_data;
+	if (devmap && devmap->device && devmap->device->discipline)
+		dname = devmap->device->discipline->name;
+	spin_unlock(&dasd_devmap_lock);
+	return snprintf(buf, PAGE_SIZE, "%s\n", dname);
+}
+
+static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
+
+static struct attribute * dasd_attrs[] = {
+	&dev_attr_readonly.attr,
+	&dev_attr_discipline.attr,
+	&dev_attr_use_diag.attr,
+	NULL,
+};
+
+static struct attribute_group dasd_attr_group = {
+	.attrs = dasd_attrs,
+};
+
+int
+dasd_add_sysfs_files(struct ccw_device *cdev)
+{
+	return sysfs_create_group(&cdev->dev.kobj, &dasd_attr_group);
+}
+
 int
 dasd_devmap_init(void)
 {
diff -puN drivers/s390/block/dasd_diag.c~s390-04-dasd-driver drivers/s390/block/dasd_diag.c
--- 25/drivers/s390/block/dasd_diag.c~s390-04-dasd-driver	Thu Jan  8 14:11:30 2004
+++ 25-akpm/drivers/s390/block/dasd_diag.c	Thu Jan  8 14:11:30 2004
@@ -6,7 +6,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
  *
- * $Revision: 1.32 $
+ * $Revision: 1.33 $
  */
 
 #include <linux/config.h>
@@ -393,7 +393,7 @@ dasd_diag_build_cp(struct dasd_device * 
 	recid = first_rec;
 	rq_for_each_bio(bio, req) {
 		bio_for_each_segment(bv, bio, i) {
-			dst = kmap(bv->bv_page) + bv->bv_offset;
+			dst = page_address(bv->bv_page) + bv->bv_offset;
 			for (off = 0; off < bv->bv_len; off += blksize) {
 				memset(dbio, 0, sizeof (struct dasd_diag_bio));
 				dbio->type = rw_cmd;
diff -puN drivers/s390/block/dasd_eckd.c~s390-04-dasd-driver drivers/s390/block/dasd_eckd.c
--- 25/drivers/s390/block/dasd_eckd.c~s390-04-dasd-driver	Thu Jan  8 14:11:30 2004
+++ 25-akpm/drivers/s390/block/dasd_eckd.c	Thu Jan  8 14:11:30 2004
@@ -7,7 +7,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
  *
- * $Revision: 1.47 $
+ * $Revision: 1.49 $
  */
 
 #include <linux/config.h>
@@ -103,6 +103,7 @@ static struct ccw_driver dasd_eckd_drive
 	.remove      = dasd_generic_remove,
 	.set_offline = dasd_generic_set_offline,
 	.set_online  = dasd_eckd_set_online,
+	.notify      = dasd_generic_notify,
 };
 
 static const int sizes_trk0[] = { 28, 148, 84 };
@@ -460,8 +461,8 @@ dasd_eckd_check_characteristics(struct d
 	/* Invalidate status of initial analysis. */
 	private->init_cqr_status = -1;
 	/* Set default cache operations. */
-	private->attrib.operation = DASD_SEQ_ACCESS;
-	private->attrib.nr_cyl = 0x02;
+	private->attrib.operation = DASD_NORMAL_CACHE;
+	private->attrib.nr_cyl = 0;
 
 	/* Read Device Characteristics */
 	rdc_data = (void *) &(private->rdc_data);
@@ -1292,6 +1293,36 @@ dasd_eckd_performance(struct block_devic
 }
 
 /*
+ * Get attributes (cache operations)
+ * Returnes the cache attributes used in Define Extend (DE).
+ */
+static int
+dasd_eckd_get_attrib (struct block_device *bdev, int no, long args)
+{
+	struct dasd_device *device;
+        struct dasd_eckd_private *private;
+        struct attrib_data_t attrib;
+	int rc;
+
+        if (!capable(CAP_SYS_ADMIN))
+                return -EACCES;
+        if (!args)
+                return -EINVAL;
+
+        device = bdev->bd_disk->private_data;
+        if (device == NULL)
+                return -ENODEV;
+
+        private = (struct dasd_eckd_private *) device->private;
+        attrib = private->attrib;
+	
+        rc = copy_to_user((long *) args, (long *) &attrib,
+			  sizeof (struct attrib_data_t));
+	
+	return rc;
+}
+
+/*
  * Set attributes (cache operations)
  * Stores the attributes for cache operation to be used in Define Extend (DE).
  */
@@ -1433,6 +1464,8 @@ dasd_eckd_init(void)
 {
 	int ret;
 
+	dasd_ioctl_no_register(THIS_MODULE, BIODASDGATTR,
+			       dasd_eckd_get_attrib);
 	dasd_ioctl_no_register(THIS_MODULE, BIODASDSATTR,
 			       dasd_eckd_set_attrib);
 	dasd_ioctl_no_register(THIS_MODULE, BIODASDPSRD,
@@ -1448,6 +1481,8 @@ dasd_eckd_init(void)
 
 	ret = ccw_driver_register(&dasd_eckd_driver);
 	if (ret) {
+		dasd_ioctl_no_unregister(THIS_MODULE, BIODASDGATTR,
+					 dasd_eckd_get_attrib);
 		dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR,
 					 dasd_eckd_set_attrib);
 		dasd_ioctl_no_unregister(THIS_MODULE, BIODASDPSRD,
@@ -1470,6 +1505,8 @@ dasd_eckd_cleanup(void)
 {
 	ccw_driver_unregister(&dasd_eckd_driver);
 
+	dasd_ioctl_no_unregister(THIS_MODULE, BIODASDGATTR,
+				 dasd_eckd_get_attrib);
 	dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR,
 				 dasd_eckd_set_attrib);
 	dasd_ioctl_no_unregister(THIS_MODULE, BIODASDPSRD,
diff -puN drivers/s390/block/dasd_fba.c~s390-04-dasd-driver drivers/s390/block/dasd_fba.c
--- 25/drivers/s390/block/dasd_fba.c~s390-04-dasd-driver	Thu Jan  8 14:11:30 2004
+++ 25-akpm/drivers/s390/block/dasd_fba.c	Thu Jan  8 14:11:30 2004
@@ -4,7 +4,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
  *
- * $Revision: 1.30 $
+ * $Revision: 1.32 $
  */
 
 #include <linux/config.h>
@@ -80,6 +80,7 @@ static struct ccw_driver dasd_fba_driver
 	.remove      = dasd_generic_remove,
 	.set_offline = dasd_generic_set_offline,
 	.set_online  = dasd_fba_set_online,
+	.notify      = dasd_generic_notify,
 };
 
 static inline void
@@ -269,7 +270,7 @@ dasd_fba_build_cp(struct dasd_device * d
 				return ERR_PTR(-EINVAL);
 			count += bv->bv_len >> (device->s2b_shift + 9);
 #if defined(CONFIG_ARCH_S390X)
-			cidaw += idal_nr_words(kmap(bv->bv_page) +
+			cidaw += idal_nr_words(page_address(bv->bv_page) +
 					       bv->bv_offset, bv->bv_len);
 #endif
 		}
@@ -309,7 +310,7 @@ dasd_fba_build_cp(struct dasd_device * d
 	}
 	recid = first_rec;
 	rq_for_each_bio(bio, req) bio_for_each_segment(bv, bio, i) {
-		dst = kmap(bv->bv_page) + bv->bv_offset;
+		dst = page_address(bv->bv_page) + bv->bv_offset;
 		for (off = 0; off < bv->bv_len; off += blksize) {
 			/* Locate record for stupid devices. */
 			if (private->rdc_data.mode.bits.data_chain == 0) {
diff -puN drivers/s390/block/dasd_genhd.c~s390-04-dasd-driver drivers/s390/block/dasd_genhd.c
--- 25/drivers/s390/block/dasd_genhd.c~s390-04-dasd-driver	Thu Jan  8 14:11:30 2004
+++ 25-akpm/drivers/s390/block/dasd_genhd.c	Thu Jan  8 14:11:30 2004
@@ -9,7 +9,7 @@
  *
  * gendisk related functions for the dasd driver.
  *
- * $Revision: 1.41 $
+ * $Revision: 1.42 $
  */
 
 #include <linux/config.h>
@@ -31,7 +31,6 @@ int
 dasd_gendisk_alloc(struct dasd_device *device)
 {
 	struct gendisk *gdp;
-	int len;
 
 	/* Make sure the minor for this device exists. */
 	if (device->devindex >= DASD_PER_MAJOR)
@@ -47,22 +46,8 @@ dasd_gendisk_alloc(struct dasd_device *d
 	gdp->fops = &dasd_device_operations;
 	gdp->driverfs_dev = &device->cdev->dev;
 
-	/*
-	 * Set device name.
-	 *   dasda - dasdz : 26 devices
-	 *   dasdaa - dasdzz : 676 devices, added up = 702
-	 *   dasdaaa - dasdzzz : 17576 devices, added up = 18278
-	 */
-	len = sprintf(gdp->disk_name, "dasd");
-	if (device->devindex > 25) {
-		if (device->devindex > 701)
-			len += sprintf(gdp->disk_name + len, "%c",
-				       'a'+(((device->devindex-702)/676)%26));
-		len += sprintf(gdp->disk_name + len, "%c",
-			       'a'+(((device->devindex-26)/26)%26));
-	}
-	len += sprintf(gdp->disk_name + len, "%c", 'a'+(device->devindex%26));
-
+	/* Set device name */
+ 	sprintf(gdp->disk_name, "dasd_%s_", device->cdev->dev.bus_id);
  	sprintf(gdp->devfs_name, "dasd/%s", device->cdev->dev.bus_id);
 
 	if (device->ro_flag)
diff -puN drivers/s390/block/dasd_int.h~s390-04-dasd-driver drivers/s390/block/dasd_int.h
--- 25/drivers/s390/block/dasd_int.h~s390-04-dasd-driver	Thu Jan  8 14:11:30 2004
+++ 25-akpm/drivers/s390/block/dasd_int.h	Thu Jan  8 14:11:30 2004
@@ -6,7 +6,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
  *
- * $Revision: 1.48 $
+ * $Revision: 1.52 $
  */
 
 #ifndef DASD_INT_H
@@ -268,6 +268,7 @@ struct dasd_device {
 	unsigned int s2b_shift;		/* log2 (bp_block/512) */
 	int ro_flag;			/* read-only flag */
 	int use_diag_flag;		/* diag allowed flag */
+	int disconnect_error_flag;	/* return -EIO when disconnected */
 
 
 	/* Device discipline stuff. */
@@ -308,6 +309,8 @@ struct dasd_device {
 #define DASD_STOPPED_NOT_ACC 1         /* not accessible */
 #define DASD_STOPPED_QUIESCE 2         /* Quiesced */
 #define DASD_STOPPED_PENDING 4         /* long busy */
+#define DASD_STOPPED_DC_WAIT 8         /* disconnected, wait */
+#define DASD_STOPPED_DC_EIO  16        /* disconnected, return -EIO */
 
 
 void dasd_put_device_wake(struct dasd_device *);
@@ -458,9 +461,10 @@ void dasd_set_timer(struct dasd_device *
 void dasd_clear_timer(struct dasd_device *);
 int  dasd_cancel_req(struct dasd_ccw_req *);
 int dasd_generic_probe (struct ccw_device *, struct dasd_discipline *);
-int dasd_generic_remove (struct ccw_device *cdev);
+void dasd_generic_remove (struct ccw_device *cdev);
 int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *);
 int dasd_generic_set_offline (struct ccw_device *cdev);
+int dasd_generic_notify(struct ccw_device *, int);
 void dasd_generic_auto_online (struct ccw_driver *);
 
 /* externals in dasd_devmap.c */
@@ -474,10 +478,11 @@ void dasd_devmap_exit(void);
 struct dasd_device *dasd_create_device(struct ccw_device *);
 void dasd_delete_device(struct dasd_device *);
 
+int dasd_add_sysfs_files(struct ccw_device *);
+struct dasd_device *dasd_device_from_cdev(struct ccw_device *);
 struct dasd_device *dasd_device_from_devindex(int);
 
 int dasd_parse(void);
-int dasd_add_busid(char *, int);
 int dasd_busid_known(char *);
 
 /* externals in dasd_gendisk.c */
diff -puN drivers/s390/block/dasd_proc.c~s390-04-dasd-driver drivers/s390/block/dasd_proc.c
--- 25/drivers/s390/block/dasd_proc.c~s390-04-dasd-driver	Thu Jan  8 14:11:30 2004
+++ 25-akpm/drivers/s390/block/dasd_proc.c	Thu Jan  8 14:11:30 2004
@@ -9,7 +9,7 @@
  *
  * /proc interface for the dasd driver.
  *
- * $Revision: 1.23 $
+ * $Revision: 1.24 $
  */
 
 #include <linux/config.h>
@@ -67,15 +67,10 @@ dasd_devices_show(struct seq_file *m, vo
 		seq_printf(m, "(none)");
 	/* Print kdev. */
 	if (device->gdp)
-		seq_printf(m, " at (%3d:%3d)",
+		seq_printf(m, " at (%3d:%7d)",
 			   device->gdp->major, device->gdp->first_minor);
 	else
-		seq_printf(m, "  at (???:???)");
-	/* Print device name. */
-	if (device->gdp)
-		seq_printf(m, " is %-7s", device->gdp->disk_name);
-	else
-		seq_printf(m, " is ???????");
+		seq_printf(m, "  at (???:???????)");
 	/* Print devices features. */
 	substr = device->ro_flag ? "(ro)" : " ";
 	seq_printf(m, "%4s: ", substr);
diff -puN include/asm-s390/dasd.h~s390-04-dasd-driver include/asm-s390/dasd.h
--- 25/include/asm-s390/dasd.h~s390-04-dasd-driver	Thu Jan  8 14:11:30 2004
+++ 25-akpm/include/asm-s390/dasd.h	Thu Jan  8 14:11:30 2004
@@ -8,7 +8,7 @@
  * any future changes wrt the API will result in a change of the APIVERSION reported
  * to userspace by the DASDAPIVER-ioctl
  *
- * $Revision: 1.4 $
+ * $Revision: 1.6 $
  *
  */
 
@@ -69,9 +69,11 @@ typedef struct dasd_information2_t {
  * values to be used for dasd_information_t.features
  * 0x00: default features
  * 0x01: readonly (ro)
+ * 0x02: use diag discipline (diag)
  */
 #define DASD_FEATURE_DEFAULT  0
 #define DASD_FEATURE_READONLY 1
+#define DASD_FEATURE_USEDIAG  2
 
 #define DASD_PARTN_BITS 2
 
@@ -234,6 +236,8 @@ typedef struct attrib_data_t {
 #define BIODASDINFO2   _IOR(DASD_IOCTL_LETTER,3,dasd_information2_t)
 /* Performance Statistics Read */
 #define BIODASDPSRD    _IOR(DASD_IOCTL_LETTER,4,dasd_rssd_perf_stats_t)
+/* Get Attributes (cache operations) */
+#define BIODASDGATTR   _IOR(DASD_IOCTL_LETTER,5,attrib_data_t) 
 
 
 /* #define BIODASDFORMAT  _IOW(IOCTL_LETTER,0,format_data_t) , deprecated */
diff -puN include/linux/genhd.h~s390-04-dasd-driver include/linux/genhd.h
--- 25/include/linux/genhd.h~s390-04-dasd-driver	Thu Jan  8 14:11:30 2004
+++ 25-akpm/include/linux/genhd.h	Thu Jan  8 14:11:30 2004
@@ -82,7 +82,7 @@ struct gendisk {
 	int major;			/* major number of driver */
 	int first_minor;
 	int minors;
-	char disk_name[16];		/* name of major driver */
+	char disk_name[32];		/* name of major driver */
 	struct hd_struct **part;	/* [indexed by minor] */
 	struct block_device_operations *fops;
 	struct request_queue *queue;

_