From: Pete Zaitcev Signed-off-by: Andrew Morton --- 25-akpm/drivers/block/ub.c | 68 +++++++++++++++++++++++++++++++-------------- 1 files changed, 47 insertions(+), 21 deletions(-) diff -puN drivers/block/ub.c~ub-atomicity-fix drivers/block/ub.c --- 25/drivers/block/ub.c~ub-atomicity-fix 2005-03-24 21:56:32.000000000 -0800 +++ 25-akpm/drivers/block/ub.c 2005-03-24 21:56:32.000000000 -0800 @@ -300,6 +300,7 @@ struct ub_dev { /* */ +static void ub_cleanup(struct ub_dev *sc); static int ub_bd_rq_fn_1(struct ub_dev *sc, struct request *rq); static int ub_cmd_build_block(struct ub_dev *sc, struct ub_scsi_cmd *cmd, struct request *rq); @@ -478,21 +479,47 @@ static int ub_id_get(void) static void ub_id_put(int id) { + unsigned long flags; if (id < 0 || id >= UB_MAX_HOSTS) { printk(KERN_ERR DRV_NAME ": bad host ID %d\n", id); return; } + + spin_lock_irqsave(&ub_lock, flags); if (ub_hostv[id] == 0) { + spin_unlock_irqrestore(&ub_lock, flags); printk(KERN_ERR DRV_NAME ": freeing free host ID %d\n", id); return; } ub_hostv[id] = 0; + spin_unlock_irqrestore(&ub_lock, flags); +} + +/* + * Downcount for deallocation. This rides on two assumptions: + * - once something is poisoned, its refcount cannot grow + * - opens cannot happen at this time (del_gendisk was done) + * If the above is true, we can drop the lock, which we need for + * blk_cleanup_queue(): the silly thing may attempt to sleep. + * [Actually, it never needs to sleep for us, but it calls might_sleep()] + */ +static void ub_put(struct ub_dev *sc) +{ + unsigned long flags; + + spin_lock_irqsave(&ub_lock, flags); + --sc->openc; + if (sc->openc == 0 && atomic_read(&sc->poison)) { + spin_unlock_irqrestore(&ub_lock, flags); + ub_cleanup(sc); + } else { + spin_unlock_irqrestore(&ub_lock, flags); + } } /* * Final cleanup and deallocation. - * This must be called with ub_lock taken. */ static void ub_cleanup(struct ub_dev *sc) { @@ -1531,11 +1558,7 @@ static int ub_bd_open(struct inode *inod return 0; err_open: - spin_lock_irqsave(&ub_lock, flags); - --sc->openc; - if (sc->openc == 0 && atomic_read(&sc->poison)) - ub_cleanup(sc); - spin_unlock_irqrestore(&ub_lock, flags); + ub_put(sc); return rc; } @@ -1545,15 +1568,8 @@ static int ub_bd_release(struct inode *i { struct gendisk *disk = inode->i_bdev->bd_disk; struct ub_dev *sc = disk->private_data; - unsigned long flags; - spin_lock_irqsave(&ub_lock, flags); - --sc->openc; - if (sc->openc == 0) - sc->first_open = 0; - if (sc->openc == 0 && atomic_read(&sc->poison)) - ub_cleanup(sc); - spin_unlock_irqrestore(&ub_lock, flags); + ub_put(sc); return 0; } @@ -2048,9 +2064,7 @@ err_diag: usb_set_intfdata(intf, NULL); // usb_put_intf(sc->intf); usb_put_dev(sc->dev); - spin_lock_irq(&ub_lock); ub_id_put(sc->id); - spin_unlock_irq(&ub_lock); err_id: kfree(sc); err_core: @@ -2064,6 +2078,15 @@ static void ub_disconnect(struct usb_int unsigned long flags; /* + * Prevent ub_bd_release from pulling the rug from under us. + * XXX This is starting to look like a kref. + * XXX Why not to take this ref at probe time? + */ + spin_lock_irqsave(&ub_lock, flags); + sc->openc++; + spin_unlock_irqrestore(&ub_lock, flags); + + /* * Fence stall clearnings, operations triggered by unlinkings and so on. * We do not attempt to unlink any URBs, because we do not trust the * unlink paths in HC drivers. Also, we get -84 upon disconnect anyway. @@ -2099,10 +2122,16 @@ static void ub_disconnect(struct usb_int spin_unlock_irqrestore(&sc->lock, flags); /* - * Unregister the upper layer, this waits for all commands to end. + * Unregister the upper layer. */ if (disk->flags & GENHD_FL_UP) del_gendisk(disk); + /* + * I wish I could do: + * set_bit(QUEUE_FLAG_DEAD, &q->queue_flags); + * As it is, we rely on our internal poisoning and let + * the upper levels to spin furiously failing all the I/O. + */ /* * Taking a lock on a structure which is about to be freed @@ -2138,10 +2167,7 @@ static void ub_disconnect(struct usb_int usb_put_dev(sc->dev); sc->dev = NULL; - spin_lock_irqsave(&ub_lock, flags); - if (sc->openc == 0) - ub_cleanup(sc); - spin_unlock_irqrestore(&ub_lock, flags); + ub_put(sc); } static struct usb_driver ub_driver = { _