mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] s390 (5/8): tape driver fixes.
@ 2003-10-21 15:08 Martin Schwidefsky
  0 siblings, 0 replies; only message in thread
From: Martin Schwidefsky @ 2003-10-21 15:08 UTC (permalink / raw)
  To: torvalds, linux-kernel

 - Fix device reference counting.
 - Fix online-offline-online cycle.
 - Add module license gpl.

diffstat:
 drivers/s390/char/tape.h       |    3 -
 drivers/s390/char/tape_34xx.c  |    9 +--
 drivers/s390/char/tape_block.c |   18 +++++--
 drivers/s390/char/tape_char.c  |    7 +-
 drivers/s390/char/tape_core.c  |   98 +++++++++++++++++++++--------------------
 5 files changed, 73 insertions(+), 62 deletions(-)

diff -urN linux-2.6/drivers/s390/char/tape.h linux-2.6-s390/drivers/s390/char/tape.h
--- linux-2.6/drivers/s390/char/tape.h	Fri Oct 17 23:43:20 2003
+++ linux-2.6-s390/drivers/s390/char/tape.h	Tue Oct 21 16:37:37 2003
@@ -246,7 +246,8 @@
 extern int tape_generic_remove(struct ccw_device *);
 
 extern struct tape_device *tape_get_device(int devindex);
-extern void tape_put_device(struct tape_device *);
+extern struct tape_device *tape_get_device_reference(struct tape_device *);
+extern struct tape_device *tape_put_device(struct tape_device *);
 
 /* Externals from tape_char.c */
 extern int tapechar_init(void);
diff -urN linux-2.6/drivers/s390/char/tape_34xx.c linux-2.6-s390/drivers/s390/char/tape_34xx.c
--- linux-2.6/drivers/s390/char/tape_34xx.c	Fri Oct 17 23:43:02 2003
+++ linux-2.6-s390/drivers/s390/char/tape_34xx.c	Tue Oct 21 16:37:37 2003
@@ -104,7 +104,7 @@
 			DBF_EVENT(3, "T34XX: internal error: unknown work\n");
 	}
 
-	tape_put_device(p->device);
+	p->device = tape_put_device(p->device);
 	kfree(p);
 }
 
@@ -123,8 +123,7 @@
 	memset(p, 0, sizeof(*p));
 	INIT_WORK(&p->work, tape_34xx_work_handler, p);
 
-	atomic_inc(&device->ref_count);
-	p->device = device;
+	p->device = tape_get_device_reference(device);
 	p->op     = op;
 
 	schedule_work(&p->work);
@@ -1041,7 +1040,7 @@
 {
 	int rc;
 
-	DBF_EVENT(3, "34xx init: $Revision: 1.8 $\n");
+	DBF_EVENT(3, "34xx init: $Revision: 1.9 $\n");
 	/* Register driver for 3480/3490 tapes. */
 	rc = ccw_driver_register(&tape_34xx_driver);
 	if (rc)
@@ -1060,7 +1059,7 @@
 MODULE_DEVICE_TABLE(ccw, tape_34xx_ids);
 MODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH");
 MODULE_DESCRIPTION("Linux on zSeries channel attached 3480 tape "
-		   "device driver ($Revision: 1.8 $)");
+		   "device driver ($Revision: 1.9 $)");
 MODULE_LICENSE("GPL");
 
 module_init(tape_34xx_init);
diff -urN linux-2.6/drivers/s390/char/tape_block.c linux-2.6-s390/drivers/s390/char/tape_block.c
--- linux-2.6/drivers/s390/char/tape_block.c	Fri Oct 17 23:43:24 2003
+++ linux-2.6-s390/drivers/s390/char/tape_block.c	Tue Oct 21 16:37:37 2003
@@ -214,7 +214,8 @@
 	if (!disk)
 		return -ENOMEM;
 
-	tasklet_init(&d->tasklet, tapeblock_tasklet, (unsigned long)device);
+	tasklet_init(&d->tasklet, tapeblock_tasklet,
+		(unsigned long) tape_get_device_reference(device));
 
 	spin_lock_init(&d->request_queue_lock);
 	q = blk_init_queue(tapeblock_request_fn, &d->request_queue_lock);
@@ -239,7 +240,7 @@
 	disk->major = tapeblock_major;
 	disk->first_minor = device->first_minor;
 	disk->fops = &tapeblock_fops;
-	disk->private_data = device;
+	disk->private_data = tape_get_device_reference(device);
 	disk->queue = q;
 	//set_capacity(disk, size);
 
@@ -262,10 +263,12 @@
 	struct tape_blk_data *d = &device->blk_data;
 
 	del_gendisk(d->disk);
+	d->disk->private_data = tape_put_device(d->disk->private_data);
 	put_disk(d->disk);
 	blk_cleanup_queue(d->request_queue);
 
 	tasklet_kill(&d->tasklet);
+	tape_put_device(device);
 }
 
 /*
@@ -301,9 +304,9 @@
 static int
 tapeblock_open(struct inode *inode, struct file *filp)
 {
-	struct gendisk *disk = inode->i_bdev->bd_disk;
-	struct tape_device *device = disk->private_data;
-	int rc;
+	struct gendisk *	disk;
+	struct tape_device *	device;
+	int			rc;
 
 	/*
 	 * FIXME: this new tapeblock_open function is from 2.5.69.
@@ -311,6 +314,9 @@
 	 * pointer from disk->private_data. It is stored in 
 	 * tapeblock_setup_device but WITHOUT proper ref-counting.
 	 */
+	disk   = inode->i_bdev->bd_disk;
+	device = tape_get_device_reference(disk->private_data);
+
 	rc = tape_open(device);
 	if (rc)
 		goto put_device;
@@ -341,8 +347,8 @@
 	struct gendisk *disk = inode->i_bdev->bd_disk;
 	struct tape_device *device = disk->private_data;
 
-	tape_release(device);
 	tape_unassign(device);
+	tape_release(device);
 	tape_put_device(device);
 
 	return 0;
diff -urN linux-2.6/drivers/s390/char/tape_char.c linux-2.6-s390/drivers/s390/char/tape_char.c
--- linux-2.6/drivers/s390/char/tape_char.c	Fri Oct 17 23:43:24 2003
+++ linux-2.6-s390/drivers/s390/char/tape_char.c	Tue Oct 21 16:37:37 2003
@@ -267,8 +267,9 @@
 {
 	struct tape_device *device;
 
-	device = (struct tape_device *) filp->private_data;
 	DBF_EVENT(6, "TCHAR:release: %x\n", iminor(inode));
+	device = (struct tape_device *) filp->private_data;
+
 #if 0
 	// FIXME: this is broken. Either MTWEOF/MTWEOF/MTBSR is done
 	// EVERYTIME the user switches from write to something different
@@ -287,9 +288,9 @@
 		device->char_data.idal_buf = NULL;
 	}
 	device->char_data.block_size = 0;
-	tape_release(device);
 	tape_unassign(device);
-	tape_put_device(device);
+	tape_release(device);
+	filp->private_data = tape_put_device(device);
 	return 0;
 }
 
diff -urN linux-2.6/drivers/s390/char/tape_core.c linux-2.6-s390/drivers/s390/char/tape_core.c
--- linux-2.6/drivers/s390/char/tape_core.c	Fri Oct 17 23:42:57 2003
+++ linux-2.6-s390/drivers/s390/char/tape_core.c	Tue Oct 21 16:37:37 2003
@@ -36,11 +36,6 @@
 static rwlock_t tape_device_lock = RW_LOCK_UNLOCKED;
 
 /*
- * Wait queue for tape_delete_device waits.
- */
-static DECLARE_WAIT_QUEUE_HEAD(tape_delete_wq);
-
-/*
  * Pointer to debug area.
  */
 debug_info_t *tape_dbf_area = NULL;
@@ -258,7 +253,7 @@
 			__tape_halt_io(device, request);
 		list_del(&request->list);
 		/* Decrease ref_count for removed request. */
-		tape_put_device(device);
+		request->device = tape_put_device(device);
 		request->rc = -EIO;
 		if (request->callback != NULL)
 			request->callback(request, request->callback_data);
@@ -269,6 +264,9 @@
 	tapechar_cleanup_device(device);
 	device->discipline->cleanup_device(device);
 	tape_remove_minor(device);
+
+	/* FIXME: This should be more something like TS_OFFLINE */
+	device->tape_state = TS_INIT;
 }
 
 /*
@@ -301,17 +299,50 @@
 	device->tape_state = TS_INIT;
 	device->medium_state = MS_UNKNOWN;
 	*device->modeset_byte = 0;
+	atomic_set(&device->ref_count, 1);
+
 	return device;
 }
 
 /*
- * Free memory of a device structure.
+ * Get a reference to an existing device structure. This will automatically
+ * increment the reference count.
  */
-static void
-tape_free_device(struct tape_device *device)
+struct tape_device *
+tape_get_device_reference(struct tape_device *device)
+{
+	DBF_EVENT(4, "tape_get_device_reference(%p) = %i\n", device,
+		atomic_inc_return(&device->ref_count));
+
+	return device;
+}
+
+/*
+ * Decrease the reference counter of a devices structure. If the
+ * reference counter reaches zero free the device structure.
+ * The function returns a NULL pointer to be used by the caller
+ * for clearing reference pointers.
+ */
+struct tape_device *
+tape_put_device(struct tape_device *device)
 {
-	kfree(device->modeset_byte);
-	kfree(device);
+	int remain;
+
+	remain = atomic_dec_return(&device->ref_count);
+	if (remain > 0) {
+		DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, remain);
+	} else {
+		if (remain < 0) {
+			DBF_EVENT(4, "put device without reference\n");
+			PRINT_ERR("put device without reference\n");
+		} else {
+			DBF_EVENT(4, "tape_free_device(%p)\n", device);
+			kfree(device->modeset_byte);
+			kfree(device);
+		}
+	}
+
+	return NULL;			
 }
 
 /*
@@ -326,8 +357,7 @@
 	read_lock(&tape_device_lock);
 	list_for_each_entry(tmp, &tape_device_list, node) {
 		if (tmp->first_minor * TAPE_MINORS_PER_DEV == devindex) {
-			device = tmp;
-			atomic_inc(&device->ref_count);
+			device = tape_get_device_reference(tmp);
 			break;
 		}
 	}
@@ -336,25 +366,6 @@
 }
 
 /*
- * Decrease the reference counter of a devices structure. If the
- * reference counter reaches zero free the device structure and
- * wake up sleepers.
- */
-void
-tape_put_device(struct tape_device *device)
-{
-	if (atomic_dec_return(&device->ref_count) > 0)
-		return;
-	/*
-	 * Reference counter dropped to zero. This means
-	 * that the device is deleted and the last user
-	 * of the device structure is gone. That is what
-	 * tape_delete_device is waiting for. Do a wake up.
-	 */
-	wake_up(&tape_delete_wq);
-}
-
-/*
  * Driverfs tape probe function.
  */
 int
@@ -367,7 +378,6 @@
 	if (IS_ERR(device))
 		return -ENODEV;
 	PRINT_INFO("tape device %s found\n", bus_id);
-	atomic_inc(&device->ref_count);
 	cdev->dev.driver_data = device;
 	device->cdev = cdev;
 	cdev->handler = tape_do_irq;
@@ -383,14 +393,8 @@
 int
 tape_generic_remove(struct ccw_device *cdev)
 {
-	struct tape_device *device;
-
-	device = cdev->dev.driver_data;
-	cdev->dev.driver_data = NULL;
-	if (device != NULL) {
-		tape_put_device(device);
-		wait_event(tape_delete_wq, atomic_read(&device->ref_count) == 0);
-		tape_free_device(device);
+	if (cdev->dev.driver_data != NULL) {
+		cdev->dev.driver_data = tape_put_device(cdev->dev.driver_data);
 	}
 	return 0;
 }
@@ -446,8 +450,7 @@
 tape_free_request (struct tape_request * request)
 {
 	if (request->device != NULL) {
-		tape_put_device(request->device);
-		request->device = NULL;
+		request->device = tape_put_device(request->device);
 	}
 	if (request->cpdata != NULL)
 		kfree(request->cpdata);
@@ -518,8 +521,7 @@
 		return -ENODEV;
 
 	/* Increase use count of device for the added request. */
-	atomic_inc(&device->ref_count);
-	request->device = device;
+	request->device = tape_get_device_reference(device);
 
 	if (list_empty(&device->req_queue)) {
 		/* No other requests are on the queue. Start this one. */
@@ -905,7 +907,7 @@
 {
 	tape_dbf_area = debug_register ( "tape", 1, 2, 3*sizeof(long));
 	debug_register_view(tape_dbf_area, &debug_sprintf_view);
-	DBF_EVENT(3, "tape init: ($Revision: 1.26 $)\n");
+	DBF_EVENT(3, "tape init: ($Revision: 1.29 $)\n");
 	tape_proc_init();
 	tapechar_init ();
 	tapeblock_init ();
@@ -930,7 +932,8 @@
 MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte and "
 	      "Michael Holzheu (cotte@de.ibm.com,holzheu@de.ibm.com)");
 MODULE_DESCRIPTION("Linux on zSeries channel attached "
-		   "tape device driver ($Revision: 1.26 $)");
+		   "tape device driver ($Revision: 1.29 $)");
+MODULE_LICENSE("GPL");
 
 module_init(tape_init);
 module_exit(tape_exit);
@@ -941,6 +944,7 @@
 EXPORT_SYMBOL(tape_generic_probe);
 EXPORT_SYMBOL(tape_enable_device);
 EXPORT_SYMBOL(tape_put_device);
+EXPORT_SYMBOL(tape_get_device_reference);
 EXPORT_SYMBOL(tape_state_verbose);
 EXPORT_SYMBOL(tape_op_verbose);
 EXPORT_SYMBOL(tape_state_set);

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2003-10-21 15:10 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-10-21 15:08 [PATCH] s390 (5/8): tape driver fixes Martin Schwidefsky

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®