From: Kai Makisara The patch fixes the following problems: - cdev allocation moved outside st_dev_arr_lock, prevents the debugging error messages reported by Mike and Mike - cdev_unmap() added before cdev_del(), prevents oops (and kernel data corruption) in case someone tries to use a device after removing the module - better error handling for failures in cdev allocation --- drivers/scsi/st.c | 112 +++++++++++++++++++++++++++++++----------------------- 1 files changed, 66 insertions(+), 46 deletions(-) diff -puN drivers/scsi/st.c~scsi-tape-fixes drivers/scsi/st.c --- 25/drivers/scsi/st.c~scsi-tape-fixes 2004-01-24 01:00:26.000000000 -0800 +++ 25-akpm/drivers/scsi/st.c 2004-01-24 01:00:26.000000000 -0800 @@ -9,7 +9,7 @@ Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky, Michael Schaefer, J"org Weule, and Eric Youngdale. - Copyright 1992 - 2003 Kai Makisara + Copyright 1992 - 2004 Kai Makisara email Kai.Makisara@kolumbus.fi Some small formal changes - aeb, 950809 @@ -17,7 +17,7 @@ Last modified: 18-JAN-1998 Richard Gooch Devfs support */ -static char *verstr = "20031228"; +static char *verstr = "20040122"; #include @@ -3846,27 +3846,53 @@ static int st_probe(struct device *dev) STm->default_compression = ST_DONT_TOUCH; STm->default_blksize = (-1); /* No forced size */ STm->default_density = (-1); /* No forced density */ + } + + for (i = 0; i < ST_NBR_PARTITIONS; i++) { + STps = &(tpnt->ps[i]); + STps->rw = ST_IDLE; + STps->eof = ST_NOEOF; + STps->at_sm = 0; + STps->last_block_valid = FALSE; + STps->drv_block = (-1); + STps->drv_file = (-1); + } + + tpnt->current_mode = 0; + tpnt->modes[0].defined = TRUE; + tpnt->density_changed = tpnt->compression_changed = + tpnt->blksize_changed = FALSE; + init_MUTEX(&tpnt->lock); + + st_nr_dev++; + write_unlock(&st_dev_arr_lock); + + for (mode = 0; mode < ST_NBR_MODES; ++mode) { + STm = &(tpnt->modes[mode]); for (j=0; j < 2; j++) { cdev = cdev_alloc(); if (!cdev) { printk(KERN_ERR - "st: out of memory. Device not attached.\n"); - goto out_put_disk; + "st%d: out of memory. Device not attached.\n", + dev_num); + goto out_free_tape; } snprintf(cdev->kobj.name, KOBJ_NAME_LEN, "%sm%d%s", disk->disk_name, - i, j ? "n" : ""); + mode, j ? "n" : ""); cdev->owner = THIS_MODULE; cdev->ops = &st_fops; - STm->cdevs[j] = cdev; - error = cdev_add(STm->cdevs[j], - MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(dev_num, i, j)), + error = cdev_add(cdev, + MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(dev_num, mode, j)), 1); if (error) { printk(KERN_ERR "st%d: Can't add %s-rewind mode %d\n", - dev_num, j ? "non" : "auto", i); + dev_num, j ? "non" : "auto", mode); + printk(KERN_ERR "st%d: Device not attached.\n", dev_num); + goto out_free_tape; } + STm->cdevs[j] = cdev; error = sysfs_create_link(&STm->cdevs[j]->kobj, &SDp->sdev_gendev.kobj, "device"); @@ -3884,35 +3910,15 @@ static int st_probe(struct device *dev) dev_num); } - for (i = 0; i < ST_NBR_PARTITIONS; i++) { - STps = &(tpnt->ps[i]); - STps->rw = ST_IDLE; - STps->eof = ST_NOEOF; - STps->at_sm = 0; - STps->last_block_valid = FALSE; - STps->drv_block = (-1); - STps->drv_file = (-1); - } - - tpnt->current_mode = 0; - tpnt->modes[0].defined = TRUE; - - tpnt->density_changed = tpnt->compression_changed = - tpnt->blksize_changed = FALSE; - init_MUTEX(&tpnt->lock); - - st_nr_dev++; - write_unlock(&st_dev_arr_lock); - for (mode = 0; mode < ST_NBR_MODES; ++mode) { - /* Rewind entry */ - devfs_mk_cdev(MKDEV(SCSI_TAPE_MAJOR, dev_num + (mode << 5)), - S_IFCHR | S_IRUGO | S_IWUGO, - "%s/mt%s", SDp->devfs_name, st_formats[mode]); - /* No-rewind entry */ - devfs_mk_cdev(MKDEV(SCSI_TAPE_MAJOR, dev_num + (mode << 5) + 128), - S_IFCHR | S_IRUGO | S_IWUGO, - "%s/mt%sn", SDp->devfs_name, st_formats[mode]); + /* Rewind entry */ + devfs_mk_cdev(MKDEV(SCSI_TAPE_MAJOR, dev_num + (mode << 5)), + S_IFCHR | S_IRUGO | S_IWUGO, + "%s/mt%s", SDp->devfs_name, st_formats[mode]); + /* No-rewind entry */ + devfs_mk_cdev(MKDEV(SCSI_TAPE_MAJOR, dev_num + (mode << 5) + 128), + S_IFCHR | S_IRUGO | S_IWUGO, + "%s/mt%sn", SDp->devfs_name, st_formats[mode]); } disk->number = devfs_register_tape(SDp->devfs_name); @@ -3924,18 +3930,30 @@ static int st_probe(struct device *dev) return 0; +out_free_tape: + for (mode=0; mode < ST_NBR_MODES; mode++) { + STm = &(tpnt->modes[mode]); + for (j=0; j < 2; j++) { + if (STm->cdevs[j]) { + if (cdev == STm->cdevs[j]) + cdev = NULL; + sysfs_remove_link(&STm->cdevs[j]->kobj, "device"); + cdev_unmap(MKDEV(SCSI_TAPE_MAJOR, + TAPE_MINOR(dev_num, mode, j)), 1); + cdev_del(STm->cdevs[j]); + } + } + } + if (cdev) + kobject_put(&cdev->kobj); + write_lock(&st_dev_arr_lock); + scsi_tapes[dev_num] = NULL; + st_nr_dev--; + write_unlock(&st_dev_arr_lock); out_put_disk: put_disk(disk); - if (tpnt) { - for (i=0; i < ST_NBR_MODES; i++) { - STm = &(tpnt->modes[i]); - if (STm->cdevs[0]) - kobject_put(&STm->cdevs[0]->kobj); - if (STm->cdevs[1]) - kobject_put(&STm->cdevs[1]->kobj); - } + if (tpnt) kfree(tpnt); - } out_buffer_free: kfree(buffer); out: @@ -3964,6 +3982,8 @@ static int st_remove(struct device *dev) for (j=0; j < 2; j++) { sysfs_remove_link(&tpnt->modes[mode].cdevs[j]->kobj, "device"); + cdev_unmap(MKDEV(SCSI_TAPE_MAJOR, + TAPE_MINOR(i, mode, j)), 1); cdev_del(tpnt->modes[mode].cdevs[j]); tpnt->modes[mode].cdevs[j] = NULL; } _