Description: bsg_release may crash while decrementing reference to the parent device with the following stack: [16834.636216,07] Call Trace: ... scsi_proc_hostdir_rm [16834.641944,07] [<ffffffff8141723f>] scsi_host_dev_release+0x3f/0x130 [16834.647740,07] [<ffffffff813e4f82>] device_release+0x32/0xa0 [16834.653423,07] [<ffffffff812dc6c7>] kobject_cleanup+0x77/0x190 [16834.659002,07] [<ffffffff812dc585>] kobject_put+0x25/0x50 [16834.664430,07] [<ffffffff813e5277>] put_device+0x17/0x20 [16834.669740,07] [<ffffffff812d0334>] bsg_kref_release_function+0x24/0x30 [16834.675007,07] [<ffffffff812d14a6>] bsg_release+0x166/0x1d0 [16834.680148,07] [<ffffffff8119ba2b>] __fput+0xcb/0x1d0 [16834.685156,07] [<ffffffff8119bb6e>] ____fput+0xe/0x10 [16834.690017,07] [<ffffffff81077476>] task_work_run+0x86/0xb0 [16834.694781,07] [<ffffffff81057043>] exit_to_usermode_loop+0x6b/0x9a [16834.699466,07] [<ffffffff81002875>] syscall_return_slowpath+0x55/0x60 [16834.704110,07] [<ffffffff8172d615>] int_ret_from_sys_call+0x25/0x9f if the parent driver implementing the device has unloaded. To address the problem, taking a reference to the parent driver module. Note: this is a follow-up to earlier discussion "[PATCH] Waiting for scsi_host_template release". --- block/bsg.c | 31 +++++++++++++++++++++++++++---- drivers/scsi/scsi_transport_fc.c | 3 ++- include/linux/bsg.h | 5 +++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/block/bsg.c b/block/bsg.c index b9a5361..0aa7d74 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -798,7 +798,8 @@ found: return bd; } -static struct bsg_device *bsg_get_device(struct inode *inode, struct file *file) +static struct bsg_device *bsg_get_device(struct inode *inode, struct file *file, + struct bsg_class_device **pbcd) { struct bsg_device *bd; struct bsg_class_device *bcd; @@ -814,6 +815,7 @@ static struct bsg_device *bsg_get_device(struct inode *inode, struct file *file) if (!bcd) return ERR_PTR(-ENODEV); + *pbcd = bcd; bd = __bsg_get_device(iminor(inode), bcd->queue); if (bd) @@ -829,22 +831,34 @@ static struct bsg_device *bsg_get_device(struct inode *inode, struct file *file) static int bsg_open(struct inode *inode, struct file *file) { struct bsg_device *bd; + struct bsg_class_device *bcd; - bd = bsg_get_device(inode, file); + bd = bsg_get_device(inode, file, &bcd); if (IS_ERR(bd)) return PTR_ERR(bd); file->private_data = bd; + if (bcd->parent_module) { + if (!try_module_get(bcd->parent_module)) { + bsg_put_device(bd); + return -ENODEV; + } + } return 0; } static int bsg_release(struct inode *inode, struct file *file) { + int ret; struct bsg_device *bd = file->private_data; + struct module *parent_module = bd->queue->bsg_dev.parent_module; file->private_data = NULL; - return bsg_put_device(bd); + ret = bsg_put_device(bd); + if (parent_module) + module_put(parent_module); + return ret; } static unsigned int bsg_poll(struct file *file, poll_table *wait) @@ -977,6 +991,14 @@ EXPORT_SYMBOL_GPL(bsg_unregister_queue); int bsg_register_queue(struct request_queue *q, struct device *parent, const char *name, void (*release)(struct device *)) { + return bsg_register_queue_ex(q, parent, name, release, NULL); +} +EXPORT_SYMBOL_GPL(bsg_register_queue); + +int bsg_register_queue_ex(struct request_queue *q, struct device *parent, + const char *name, void (*release)(struct device *), + struct module *parent_module) +{ struct bsg_class_device *bcd; dev_t dev; int ret; @@ -1012,6 +1034,7 @@ int bsg_register_queue(struct request_queue *q, struct device *parent, bcd->queue = q; bcd->parent = get_device(parent); bcd->release = release; + bcd->parent_module = parent_module; kref_init(&bcd->ref); dev = MKDEV(bsg_major, bcd->minor); class_dev = device_create(bsg_class, parent, dev, NULL, "%s", devname); @@ -1039,7 +1062,7 @@ unlock: mutex_unlock(&bsg_mutex); return ret; } -EXPORT_SYMBOL_GPL(bsg_register_queue); +EXPORT_SYMBOL_GPL(bsg_register_queue_ex); static struct cdev bsg_cdev; diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 24eaaf6..c153f80 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -4064,7 +4064,8 @@ fc_bsg_hostadd(struct Scsi_Host *shost, struct fc_host_attrs *fc_host) blk_queue_rq_timed_out(q, fc_bsg_job_timeout); blk_queue_rq_timeout(q, FC_DEFAULT_BSG_TIMEOUT); - err = bsg_register_queue(q, dev, bsg_name, NULL); + err = bsg_register_queue_ex(q, dev, bsg_name, NULL, + shost->hostt->module); if (err) { printk(KERN_ERR "fc_host%d: bsg interface failed to " "initialize - register queue\n", diff --git a/include/linux/bsg.h b/include/linux/bsg.h index 7173f6e..fe41e83 100644 --- a/include/linux/bsg.h +++ b/include/linux/bsg.h @@ -12,11 +12,16 @@ struct bsg_class_device { struct request_queue *queue; struct kref ref; void (*release)(struct device *); + struct module *parent_module; }; extern int bsg_register_queue(struct request_queue *q, struct device *parent, const char *name, void (*release)(struct device *)); +extern int bsg_register_queue_ex(struct request_queue *q, + struct device *parent, const char *name, + void (*release)(struct device *), + struct module *parent_module); extern void bsg_unregister_queue(struct request_queue *); #else static inline int bsg_register_queue(struct request_queue *q, -- 1.9.1