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

block device driver changes:
 - dasd: Fix diag discipline if it is loaded as a module.
 - dcssblk: Replace r/w lock with r/w semaphore to be able to call
   device_register inside a critical section.
 - dcssblk: Fix error handling in write function for dcss "add" attribute.
 - xpram & dcssblk: Fix sanity check for sector number.

Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/s390/block/dasd.c      |    6 +
 25-akpm/drivers/s390/block/dasd_diag.c |    8 +-
 25-akpm/drivers/s390/block/dasd_int.h  |    9 --
 25-akpm/drivers/s390/block/dcssblk.c   |  114 +++++++++++++++------------------
 25-akpm/drivers/s390/block/xpram.c     |    2 
 5 files changed, 68 insertions(+), 71 deletions(-)

diff -puN drivers/s390/block/dasd.c~s390-3-4-block-device-driver drivers/s390/block/dasd.c
--- 25/drivers/s390/block/dasd.c~s390-3-4-block-device-driver	Wed Jun  2 14:34:31 2004
+++ 25-akpm/drivers/s390/block/dasd.c	Wed Jun  2 14:34:31 2004
@@ -7,7 +7,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
  *
- * $Revision: 1.141 $
+ * $Revision: 1.142 $
  */
 
 #include <linux/config.h>
@@ -37,6 +37,7 @@
  * SECTION: exported variables of dasd.c
  */
 debug_info_t *dasd_debug_area;
+struct dasd_discipline *dasd_diag_discipline_pointer;
 
 MODULE_AUTHOR("Holger Smolinski <Holger.Smolinski@de.ibm.com>");
 MODULE_DESCRIPTION("Linux on S/390 DASD device driver,"
@@ -1990,6 +1991,8 @@ dasd_init(void)
 
 	DBF_EVENT(DBF_EMERG, "%s", "debug area created");
 
+	dasd_diag_discipline_pointer = NULL;
+
 	rc = devfs_mk_dir("dasd");
 	if (rc)
 		goto failed;
@@ -2022,6 +2025,7 @@ module_init(dasd_init);
 module_exit(dasd_exit);
 
 EXPORT_SYMBOL(dasd_debug_area);
+EXPORT_SYMBOL(dasd_diag_discipline_pointer);
 
 EXPORT_SYMBOL(dasd_add_request_head);
 EXPORT_SYMBOL(dasd_add_request_tail);
diff -puN drivers/s390/block/dasd_diag.c~s390-3-4-block-device-driver drivers/s390/block/dasd_diag.c
--- 25/drivers/s390/block/dasd_diag.c~s390-3-4-block-device-driver	Wed Jun  2 14:34:31 2004
+++ 25-akpm/drivers/s390/block/dasd_diag.c	Wed Jun  2 14:34:31 2004
@@ -6,7 +6,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
  *
- * $Revision: 1.34 $
+ * $Revision: 1.36 $
  */
 
 #include <linux/config.h>
@@ -35,6 +35,8 @@
 
 MODULE_LICENSE("GPL");
 
+struct dasd_discipline dasd_diag_discipline;
+
 struct dasd_diag_private {
 	struct dasd_diag_characteristics rdc_data;
 	struct dasd_diag_rw_io iob;
@@ -292,7 +294,7 @@ dasd_diag_check_device(struct dasd_devic
 		mdsk_term_io(device);
 	}
 	if (bsize <= PAGE_SIZE && label[3] == bsize &&
-	    label[0] == 0xc3d4e2f1 && label[13] != 0) {
+	    label[0] == 0xc3d4e2f1) {
 		device->blocks = label[7];
 		device->bp_block = bsize;
 		device->s2b_shift = 0;	/* bits to shift 512 to get a block */
@@ -489,6 +491,7 @@ dasd_diag_init(void)
 
 	ctl_set_bit(0, 9);
 	register_external_interrupt(0x2603, dasd_ext_handler);
+	dasd_diag_discipline_pointer = &dasd_diag_discipline;
 	return 0;
 }
 
@@ -503,6 +506,7 @@ dasd_diag_cleanup(void)
 	}
 	unregister_external_interrupt(0x2603, dasd_ext_handler);
 	ctl_clear_bit(0, 9);
+	dasd_diag_discipline_pointer = NULL;
 }
 
 module_init(dasd_diag_init);
diff -puN drivers/s390/block/dasd_int.h~s390-3-4-block-device-driver drivers/s390/block/dasd_int.h
--- 25/drivers/s390/block/dasd_int.h~s390-3-4-block-device-driver	Wed Jun  2 14:34:31 2004
+++ 25-akpm/drivers/s390/block/dasd_int.h	Wed Jun  2 14:34:31 2004
@@ -6,7 +6,7 @@
  * Bugreports.to..: <Linux390@de.ibm.com>
  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
  *
- * $Revision: 1.57 $
+ * $Revision: 1.58 $
  */
 
 #ifndef DASD_INT_H
@@ -260,12 +260,7 @@ struct dasd_discipline {
 	int (*fill_info) (struct dasd_device *, struct dasd_information2_t *);
 };
 
-extern struct dasd_discipline dasd_diag_discipline;
-#ifdef CONFIG_DASD_DIAG
-#define dasd_diag_discipline_pointer (&dasd_diag_discipline)
-#else
-#define dasd_diag_discipline_pointer (0)
-#endif
+extern struct dasd_discipline *dasd_diag_discipline_pointer;
 
 struct dasd_device {
 	/* Block device stuff. */
diff -puN drivers/s390/block/dcssblk.c~s390-3-4-block-device-driver drivers/s390/block/dcssblk.c
--- 25/drivers/s390/block/dcssblk.c~s390-3-4-block-device-driver	Wed Jun  2 14:34:31 2004
+++ 25-akpm/drivers/s390/block/dcssblk.c	Wed Jun  2 14:34:31 2004
@@ -76,8 +76,7 @@ struct dcssblk_dev_info {
 };
 
 static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices);
-static rwlock_t dcssblk_devices_lock = RW_LOCK_UNLOCKED;
-
+static struct rw_semaphore dcssblk_devices_sem;
 
 /*
  * release function for segment device.
@@ -92,8 +91,8 @@ dcssblk_release_segment(struct device *d
 
 /*
  * get a minor number. needs to be called with
- * write_lock(&dcssblk_devices_lock) and the
- * device needs to be enqueued before the lock is
+ * down_write(&dcssblk_devices_sem) and the
+ * device needs to be enqueued before the semaphore is
  * freed.
  */
 static inline int
@@ -121,7 +120,7 @@ dcssblk_assign_free_minor(struct dcssblk
 /*
  * get the struct dcssblk_dev_info from dcssblk_devices
  * for the given name.
- * read_lock(&dcssblk_devices_lock) must be held.
+ * down_read(&dcssblk_devices_sem) must be held.
  */
 static struct dcssblk_dev_info *
 dcssblk_get_device_by_name(char *name)
@@ -137,31 +136,6 @@ dcssblk_get_device_by_name(char *name)
 }
 
 /*
- * register the device that represents a segment in sysfs,
- * also add the attributes for the device
- */
-static inline int
-dcssblk_register_segment_device(struct device *dev)
-{
-	int rc;
-
-	rc = device_register(dev);
-	if (rc)
-		return rc;
-	rc = device_create_file(dev, &dev_attr_shared);
-	if (rc)
-		goto unregister_dev;
-	rc = device_create_file(dev, &dev_attr_save);
-	if (rc)
-		goto unregister_dev;
-	return rc;
-
-unregister_dev:
-	device_unregister(dev);
-	return rc;
-}
-
-/*
  * device attribute for switching shared/nonshared (exclusive)
  * operation (show + store)
  */
@@ -184,24 +158,24 @@ dcssblk_shared_store(struct device *dev,
 		PRINT_WARN("Invalid value, must be 0 or 1\n");
 		return -EINVAL;
 	}
-	write_lock(&dcssblk_devices_lock);
+	down_write(&dcssblk_devices_sem);
 	dev_info = container_of(dev, struct dcssblk_dev_info, dev);
 	if (atomic_read(&dev_info->use_count)) {
 		PRINT_ERR("share: segment %s is busy!\n",
 			  dev_info->segment_name);
-		write_unlock(&dcssblk_devices_lock);
+		up_write(&dcssblk_devices_sem);
 		return -EBUSY;
 	}
 	if ((inbuf[0] == '1') && (dev_info->is_shared == 1)) {
 		PRINT_WARN("Segment %s already loaded in shared mode!\n",
 			   dev_info->segment_name);
-		write_unlock(&dcssblk_devices_lock);
+		up_write(&dcssblk_devices_sem);
 		return count;
 	}
 	if ((inbuf[0] == '0') && (dev_info->is_shared == 0)) {
 		PRINT_WARN("Segment %s already loaded in exclusive mode!\n",
 			   dev_info->segment_name);
-		write_unlock(&dcssblk_devices_lock);
+		up_write(&dcssblk_devices_sem);
 		return count;
 	}
 	if (inbuf[0] == '1') {
@@ -231,7 +205,7 @@ dcssblk_shared_store(struct device *dev,
 		PRINT_INFO("Segment %s reloaded, exclusive (read-write) mode.\n",
 			   dev_info->segment_name);
 	} else {
-		write_unlock(&dcssblk_devices_lock);
+		up_write(&dcssblk_devices_sem);
 		PRINT_WARN("Invalid value, must be 0 or 1\n");
 		return -EINVAL;
 	}
@@ -262,14 +236,13 @@ dcssblk_shared_store(struct device *dev,
 				dev_info->segment_name);
 		rc = -EPERM;
 	}
-	write_unlock(&dcssblk_devices_lock);
+	up_write(&dcssblk_devices_sem);
 	goto out;
 
 removeseg:
 	PRINT_ERR("Could not reload segment %s, removing it now!\n",
 			dev_info->segment_name);
 	list_del(&dev_info->lh);
-	write_unlock(&dcssblk_devices_lock);
 
 	del_gendisk(dev_info->gd);
 	blk_put_queue(dev_info->dcssblk_queue);
@@ -277,6 +250,7 @@ removeseg:
 	put_disk(dev_info->gd);
 	device_unregister(dev);
 	put_device(dev);
+	up_write(&dcssblk_devices_sem);
 out:
 	return rc;
 }
@@ -308,7 +282,7 @@ dcssblk_save_store(struct device *dev, c
 	}
 	dev_info = container_of(dev, struct dcssblk_dev_info, dev);
 
-	write_lock(&dcssblk_devices_lock);
+	down_write(&dcssblk_devices_sem);
 	if (inbuf[0] == '1') {
 		if (atomic_read(&dev_info->use_count) == 0) {
 			// device is idle => we save immediately
@@ -332,11 +306,11 @@ dcssblk_save_store(struct device *dev, c
 					dev_info->segment_name);
 		}
 	} else {
-		write_unlock(&dcssblk_devices_lock);
+		up_write(&dcssblk_devices_sem);
 		PRINT_WARN("Invalid value, must be 0 or 1\n");
 		return -EINVAL;
 	}
-	write_unlock(&dcssblk_devices_lock);
+	up_write(&dcssblk_devices_sem);
 	return count;
 }
 
@@ -375,9 +349,9 @@ dcssblk_add_store(struct device *dev, co
 	/*
 	 * already loaded?
 	 */
-	read_lock(&dcssblk_devices_lock);
+	down_read(&dcssblk_devices_sem);
 	dev_info = dcssblk_get_device_by_name(local_buf);
-	read_unlock(&dcssblk_devices_lock);
+	up_read(&dcssblk_devices_sem);
 	if (dev_info != NULL) {
 		PRINT_WARN("Segment %s already loaded!\n", local_buf);
 		rc = -EEXIST;
@@ -433,10 +407,10 @@ dcssblk_add_store(struct device *dev, co
 	/*
 	 * get minor, add to list
 	 */
-	write_lock(&dcssblk_devices_lock);
+	down_write(&dcssblk_devices_sem);
 	rc = dcssblk_assign_free_minor(dev_info);
 	if (rc) {
-		write_unlock(&dcssblk_devices_lock);
+		up_write(&dcssblk_devices_sem);
 		PRINT_ERR("No free minor number available! "
 			  "Unloading segment...\n");
 		goto unload_seg;
@@ -444,22 +418,29 @@ dcssblk_add_store(struct device *dev, co
 	sprintf(dev_info->gd->disk_name, "dcssblk%d",
 		dev_info->gd->first_minor);
 	list_add_tail(&dev_info->lh, &dcssblk_devices);
+
+	if (!try_module_get(THIS_MODULE)) {
+		rc = -ENODEV;
+		goto list_del;
+	}
 	/*
 	 * register the device
 	 */
-	rc = dcssblk_register_segment_device(&dev_info->dev);
+	rc = device_register(&dev_info->dev);
 	if (rc) {
 		PRINT_ERR("Segment %s could not be registered RC=%d\n",
 				local_buf, rc);
+		module_put(THIS_MODULE);
 		goto list_del;
 	}
-
-	if (!try_module_get(THIS_MODULE)) {
-		rc = -ENODEV;
-		goto list_del;
-	}
-
 	get_device(&dev_info->dev);
+	rc = device_create_file(&dev_info->dev, &dev_attr_shared);
+	if (rc)
+		goto unregister_dev;
+	rc = device_create_file(&dev_info->dev, &dev_attr_save);
+	if (rc)
+		goto unregister_dev;
+
 	add_disk(dev_info->gd);
 
 	blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
@@ -476,13 +457,24 @@ dcssblk_add_store(struct device *dev, co
 			break;
 	}
 	PRINT_DEBUG("Segment %s loaded successfully\n", local_buf);
-	write_unlock(&dcssblk_devices_lock);
+	up_write(&dcssblk_devices_sem);
 	rc = count;
 	goto out;
 
+unregister_dev:
+	PRINT_ERR("device_create_file() failed!\n");
+	list_del(&dev_info->lh);
+	blk_put_queue(dev_info->dcssblk_queue);
+	dev_info->gd->queue = NULL;
+	put_disk(dev_info->gd);
+	device_unregister(&dev_info->dev);
+	segment_unload(dev_info->segment_name);
+	put_device(&dev_info->dev);
+	up_write(&dcssblk_devices_sem);
+	goto out;
 list_del:
 	list_del(&dev_info->lh);
-	write_unlock(&dcssblk_devices_lock);
+	up_write(&dcssblk_devices_sem);
 unload_seg:
 	segment_unload(local_buf);
 dealloc_gendisk:
@@ -526,22 +518,21 @@ dcssblk_remove_store(struct device *dev,
 		goto out_buf;
 	}
 
-	write_lock(&dcssblk_devices_lock);
+	down_write(&dcssblk_devices_sem);
 	dev_info = dcssblk_get_device_by_name(local_buf);
 	if (dev_info == NULL) {
-		write_unlock(&dcssblk_devices_lock);
+		up_write(&dcssblk_devices_sem);
 		PRINT_WARN("Segment %s is not loaded!\n", local_buf);
 		rc = -ENODEV;
 		goto out_buf;
 	}
 	if (atomic_read(&dev_info->use_count) != 0) {
-		write_unlock(&dcssblk_devices_lock);
+		up_write(&dcssblk_devices_sem);
 		PRINT_WARN("Segment %s is in use!\n", local_buf);
 		rc = -EBUSY;
 		goto out_buf;
 	}
 	list_del(&dev_info->lh);
-	write_unlock(&dcssblk_devices_lock);
 
 	del_gendisk(dev_info->gd);
 	blk_put_queue(dev_info->dcssblk_queue);
@@ -552,6 +543,8 @@ dcssblk_remove_store(struct device *dev,
 	PRINT_DEBUG("Segment %s unloaded successfully\n",
 			dev_info->segment_name);
 	put_device(&dev_info->dev);
+	up_write(&dcssblk_devices_sem);
+
 	rc = count;
 out_buf:
 	kfree(local_buf);
@@ -587,7 +580,7 @@ dcssblk_release(struct inode *inode, str
 		rc = -ENODEV;
 		goto out;
 	}
-	write_lock(&dcssblk_devices_lock);
+	down_write(&dcssblk_devices_sem);
 	if (atomic_dec_and_test(&dev_info->use_count)
 	    && (dev_info->save_pending)) {
 		PRINT_INFO("Segment %s became idle and is being saved now\n",
@@ -595,7 +588,7 @@ dcssblk_release(struct inode *inode, str
 		segment_replace(dev_info->segment_name);
 		dev_info->save_pending = 0;
 	}
-	write_unlock(&dcssblk_devices_lock);
+	up_write(&dcssblk_devices_sem);
 	rc = 0;
 out:
 	return rc;
@@ -616,7 +609,7 @@ dcssblk_make_request(request_queue_t *q,
 	dev_info = bio->bi_bdev->bd_disk->private_data;
 	if (dev_info == NULL)
 		goto fail;
-	if ((bio->bi_sector & 3) != 0 || (bio->bi_size & 4095) != 0)
+	if ((bio->bi_sector & 7) != 0 || (bio->bi_size & 4095) != 0)
 		/* Request is not page-aligned. */
 		goto fail;
 	if (((bio->bi_size >> 9) + bio->bi_sector)
@@ -695,6 +688,7 @@ dcssblk_init(void)
 		return rc;
 	}
 	dcssblk_major = rc;
+	init_rwsem(&dcssblk_devices_sem);
 	PRINT_DEBUG("...finished!\n");
 	return 0;
 }
diff -puN drivers/s390/block/xpram.c~s390-3-4-block-device-driver drivers/s390/block/xpram.c
--- 25/drivers/s390/block/xpram.c~s390-3-4-block-device-driver	Wed Jun  2 14:34:31 2004
+++ 25-akpm/drivers/s390/block/xpram.c	Wed Jun  2 14:34:31 2004
@@ -290,7 +290,7 @@ static int xpram_make_request(request_qu
 	unsigned long bytes;
 	int i;
 
-	if ((bio->bi_sector & 3) != 0 || (bio->bi_size & 4095) != 0)
+	if ((bio->bi_sector & 7) != 0 || (bio->bi_size & 4095) != 0)
 		/* Request is not page-aligned. */
 		goto fail;
 	if ((bio->bi_size >> 12) > xdev->size)
_