Message ID | 20180427000059.GA7044@xldev-tmpl.dev.purestorage.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Any comments on the new patch (which, I think, addresses the concern about module being stuck in unloadable state forever; if not, there would be a leak in the bsg layer)? Or on dropping a reference to bsg_class_device's parent early before the bsg_class_device itself is gone, to implement James's idea of cutting of the bsg layer at fc_bsg_remove time? Thanks. On Thu, Apr 26, 2018 at 06:01:00PM -0600, Anatoliy Glagolev wrote: > Any thoughts on this? Can we really drop a reference from a child device > (bsg_class_device) to a parent device (Scsi_Host) while the child device > is still around at fc_bsg_remove time? > > If not, please consider a fix with module references. I realized that > the previous version of the fix had a problem since bsg_open may run > more often than bsg_release. Sending a newer version... The new fix > piggybacks on the bsg layer logic allocating/freeing bsg_device structs. > When all those structs are gone there are no references to Scsi_Host from > the user-mode side. The only remaining references are from a SCSI bus > driver (like qla2xxx) itself; it is safe to drop the module reference > at that time. > > > From c744d4fd93578545ad12faa35a3354364793b124 Mon Sep 17 00:00:00 2001 > From: Anatoliy Glagolev <glagolig@gmail.com> > Date: Wed, 25 Apr 2018 19:16:10 -0600 > Subject: [PATCH] bsg referencing parent module > Signed-off-by: Anatoliy Glagolev <glagolig@gmail.com> > > Fixing a bug when bsg layer holds the last reference to device > when the device's module has been unloaded. Upon dropping the > reference the device's release function may touch memory of > the unloaded module. > --- > block/bsg-lib.c | 24 ++++++++++++++++++++++-- > block/bsg.c | 22 +++++++++++++++++++++- > drivers/scsi/scsi_transport_fc.c | 8 ++++++-- > include/linux/bsg-lib.h | 4 ++++ > include/linux/bsg.h | 5 +++++ > 5 files changed, 58 insertions(+), 5 deletions(-) > > diff --git a/block/bsg-lib.c b/block/bsg-lib.c > index fc2e5ff..bb11786 100644 > --- a/block/bsg-lib.c > +++ b/block/bsg-lib.c > @@ -309,6 +309,25 @@ struct request_queue *bsg_setup_queue(struct device *dev, const char *name, > bsg_job_fn *job_fn, int dd_job_size, > void (*release)(struct device *)) > { > + return bsg_setup_queue_ex(dev, name, job_fn, dd_job_size, release, > + NULL); > +} > +EXPORT_SYMBOL_GPL(bsg_setup_queue); > + > +/** > + * bsg_setup_queue_ex - Create and add the bsg hooks so we can receive requests > + * @dev: device to attach bsg device to > + * @name: device to give bsg device > + * @job_fn: bsg job handler > + * @dd_job_size: size of LLD data needed for each job > + * @release: @dev release function > + * @dev_module: @dev's module > + */ > +struct request_queue *bsg_setup_queue_ex(struct device *dev, const char *name, > + bsg_job_fn *job_fn, int dd_job_size, > + void (*release)(struct device *), > + struct module *dev_module) > +{ > struct request_queue *q; > int ret; > > @@ -331,7 +350,8 @@ struct request_queue *bsg_setup_queue(struct device *dev, const char *name, > blk_queue_softirq_done(q, bsg_softirq_done); > blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT); > > - ret = bsg_register_queue(q, dev, name, &bsg_transport_ops, release); > + ret = bsg_register_queue_ex(q, dev, name, &bsg_transport_ops, release, > + dev_module); > if (ret) { > printk(KERN_ERR "%s: bsg interface failed to " > "initialize - register queue\n", dev->kobj.name); > @@ -343,4 +363,4 @@ struct request_queue *bsg_setup_queue(struct device *dev, const char *name, > blk_cleanup_queue(q); > return ERR_PTR(ret); > } > -EXPORT_SYMBOL_GPL(bsg_setup_queue); > +EXPORT_SYMBOL_GPL(bsg_setup_queue_ex); > diff --git a/block/bsg.c b/block/bsg.c > index defa06c..950cd31 100644 > --- a/block/bsg.c > +++ b/block/bsg.c > @@ -666,6 +666,7 @@ static int bsg_put_device(struct bsg_device *bd) > { > int ret = 0, do_free; > struct request_queue *q = bd->queue; > + struct module *parent_module = q->bsg_dev.parent_module; > > mutex_lock(&bsg_mutex); > > @@ -695,8 +696,11 @@ static int bsg_put_device(struct bsg_device *bd) > kfree(bd); > out: > kref_put(&q->bsg_dev.ref, bsg_kref_release_function); > - if (do_free) > + if (do_free) { > blk_put_queue(q); > + if (parent_module) > + module_put(parent_module); > + } > return ret; > } > > @@ -706,12 +710,19 @@ static struct bsg_device *bsg_add_device(struct inode *inode, > { > struct bsg_device *bd; > unsigned char buf[32]; > + struct module *parent_module = rq->bsg_dev.parent_module; > > if (!blk_get_queue(rq)) > return ERR_PTR(-ENXIO); > > + if (parent_module) { > + if (!try_module_get(parent_module)) > + return ERR_PTR(-ENODEV); > + } > bd = bsg_alloc_device(); > if (!bd) { > + if (parent_module) > + module_put(parent_module); > blk_put_queue(rq); > return ERR_PTR(-ENOMEM); > } > @@ -922,6 +933,14 @@ int bsg_register_queue(struct request_queue *q, struct device *parent, > const char *name, const struct bsg_ops *ops, > void (*release)(struct device *)) > { > + return bsg_register_queue_ex(q, parent, name, ops, release, NULL); > +} > + > +int bsg_register_queue_ex(struct request_queue *q, struct device *parent, > + const char *name, const struct bsg_ops *ops, > + void (*release)(struct device *), > + struct module *parent_module) > +{ > struct bsg_class_device *bcd; > dev_t dev; > int ret; > @@ -958,6 +977,7 @@ int bsg_register_queue(struct request_queue *q, struct device *parent, > bcd->parent = get_device(parent); > bcd->release = release; > bcd->ops = ops; > + 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); > diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c > index be3be0f..f21f7d2 100644 > --- a/drivers/scsi/scsi_transport_fc.c > +++ b/drivers/scsi/scsi_transport_fc.c > @@ -3772,17 +3772,21 @@ static int fc_bsg_dispatch(struct bsg_job *job) > struct fc_internal *i = to_fc_internal(shost->transportt); > struct request_queue *q; > char bsg_name[20]; > + struct module *shost_module = NULL; > > fc_host->rqst_q = NULL; > > if (!i->f->bsg_request) > return -ENOTSUPP; > > + if (shost->hostt) > + shost_module = shost->hostt->module; > + > snprintf(bsg_name, sizeof(bsg_name), > "fc_host%d", shost->host_no); > > - q = bsg_setup_queue(dev, bsg_name, fc_bsg_dispatch, i->f->dd_bsg_size, > - NULL); > + q = bsg_setup_queue_ex(dev, bsg_name, fc_bsg_dispatch, > + i->f->dd_bsg_size, NULL, shost_module); > if (IS_ERR(q)) { > dev_err(dev, > "fc_host%d: bsg interface failed to initialize - setup queue\n", > diff --git a/include/linux/bsg-lib.h b/include/linux/bsg-lib.h > index 28a7ccc..232c855 100644 > --- a/include/linux/bsg-lib.h > +++ b/include/linux/bsg-lib.h > @@ -74,6 +74,10 @@ void bsg_job_done(struct bsg_job *job, int result, > struct request_queue *bsg_setup_queue(struct device *dev, const char *name, > bsg_job_fn *job_fn, int dd_job_size, > void (*release)(struct device *)); > +struct request_queue *bsg_setup_queue_ex(struct device *dev, const char *name, > + bsg_job_fn *job_fn, int dd_job_size, > + void (*release)(struct device *), > + struct module *dev_module); > void bsg_job_put(struct bsg_job *job); > int __must_check bsg_job_get(struct bsg_job *job); > > diff --git a/include/linux/bsg.h b/include/linux/bsg.h > index 0c7dd9c..0e7c26c 100644 > --- a/include/linux/bsg.h > +++ b/include/linux/bsg.h > @@ -23,11 +23,16 @@ struct bsg_class_device { > struct kref ref; > const struct bsg_ops *ops; > void (*release)(struct device *); > + struct module *parent_module; > }; > > int bsg_register_queue(struct request_queue *q, struct device *parent, > const char *name, const struct bsg_ops *ops, > void (*release)(struct device *)); > +int bsg_register_queue_ex(struct request_queue *q, struct device *parent, > + const char *name, const struct bsg_ops *ops, > + void (*release)(struct device *), > + struct module *parent_module); > int bsg_scsi_register_queue(struct request_queue *q, struct device *parent); > void bsg_unregister_queue(struct request_queue *q); > #else > -- > 1.9.1 >
diff --git a/block/bsg-lib.c b/block/bsg-lib.c index fc2e5ff..bb11786 100644 --- a/block/bsg-lib.c +++ b/block/bsg-lib.c @@ -309,6 +309,25 @@ struct request_queue *bsg_setup_queue(struct device *dev, const char *name, bsg_job_fn *job_fn, int dd_job_size, void (*release)(struct device *)) { + return bsg_setup_queue_ex(dev, name, job_fn, dd_job_size, release, + NULL); +} +EXPORT_SYMBOL_GPL(bsg_setup_queue); + +/** + * bsg_setup_queue_ex - Create and add the bsg hooks so we can receive requests + * @dev: device to attach bsg device to + * @name: device to give bsg device + * @job_fn: bsg job handler + * @dd_job_size: size of LLD data needed for each job + * @release: @dev release function + * @dev_module: @dev's module + */ +struct request_queue *bsg_setup_queue_ex(struct device *dev, const char *name, + bsg_job_fn *job_fn, int dd_job_size, + void (*release)(struct device *), + struct module *dev_module) +{ struct request_queue *q; int ret; @@ -331,7 +350,8 @@ struct request_queue *bsg_setup_queue(struct device *dev, const char *name, blk_queue_softirq_done(q, bsg_softirq_done); blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT); - ret = bsg_register_queue(q, dev, name, &bsg_transport_ops, release); + ret = bsg_register_queue_ex(q, dev, name, &bsg_transport_ops, release, + dev_module); if (ret) { printk(KERN_ERR "%s: bsg interface failed to " "initialize - register queue\n", dev->kobj.name); @@ -343,4 +363,4 @@ struct request_queue *bsg_setup_queue(struct device *dev, const char *name, blk_cleanup_queue(q); return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(bsg_setup_queue); +EXPORT_SYMBOL_GPL(bsg_setup_queue_ex); diff --git a/block/bsg.c b/block/bsg.c index defa06c..950cd31 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -666,6 +666,7 @@ static int bsg_put_device(struct bsg_device *bd) { int ret = 0, do_free; struct request_queue *q = bd->queue; + struct module *parent_module = q->bsg_dev.parent_module; mutex_lock(&bsg_mutex); @@ -695,8 +696,11 @@ static int bsg_put_device(struct bsg_device *bd) kfree(bd); out: kref_put(&q->bsg_dev.ref, bsg_kref_release_function); - if (do_free) + if (do_free) { blk_put_queue(q); + if (parent_module) + module_put(parent_module); + } return ret; } @@ -706,12 +710,19 @@ static struct bsg_device *bsg_add_device(struct inode *inode, { struct bsg_device *bd; unsigned char buf[32]; + struct module *parent_module = rq->bsg_dev.parent_module; if (!blk_get_queue(rq)) return ERR_PTR(-ENXIO); + if (parent_module) { + if (!try_module_get(parent_module)) + return ERR_PTR(-ENODEV); + } bd = bsg_alloc_device(); if (!bd) { + if (parent_module) + module_put(parent_module); blk_put_queue(rq); return ERR_PTR(-ENOMEM); } @@ -922,6 +933,14 @@ int bsg_register_queue(struct request_queue *q, struct device *parent, const char *name, const struct bsg_ops *ops, void (*release)(struct device *)) { + return bsg_register_queue_ex(q, parent, name, ops, release, NULL); +} + +int bsg_register_queue_ex(struct request_queue *q, struct device *parent, + const char *name, const struct bsg_ops *ops, + void (*release)(struct device *), + struct module *parent_module) +{ struct bsg_class_device *bcd; dev_t dev; int ret; @@ -958,6 +977,7 @@ int bsg_register_queue(struct request_queue *q, struct device *parent, bcd->parent = get_device(parent); bcd->release = release; bcd->ops = ops; + 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); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index be3be0f..f21f7d2 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -3772,17 +3772,21 @@ static int fc_bsg_dispatch(struct bsg_job *job) struct fc_internal *i = to_fc_internal(shost->transportt); struct request_queue *q; char bsg_name[20]; + struct module *shost_module = NULL; fc_host->rqst_q = NULL; if (!i->f->bsg_request) return -ENOTSUPP; + if (shost->hostt) + shost_module = shost->hostt->module; + snprintf(bsg_name, sizeof(bsg_name), "fc_host%d", shost->host_no); - q = bsg_setup_queue(dev, bsg_name, fc_bsg_dispatch, i->f->dd_bsg_size, - NULL); + q = bsg_setup_queue_ex(dev, bsg_name, fc_bsg_dispatch, + i->f->dd_bsg_size, NULL, shost_module); if (IS_ERR(q)) { dev_err(dev, "fc_host%d: bsg interface failed to initialize - setup queue\n", diff --git a/include/linux/bsg-lib.h b/include/linux/bsg-lib.h index 28a7ccc..232c855 100644 --- a/include/linux/bsg-lib.h +++ b/include/linux/bsg-lib.h @@ -74,6 +74,10 @@ void bsg_job_done(struct bsg_job *job, int result, struct request_queue *bsg_setup_queue(struct device *dev, const char *name, bsg_job_fn *job_fn, int dd_job_size, void (*release)(struct device *)); +struct request_queue *bsg_setup_queue_ex(struct device *dev, const char *name, + bsg_job_fn *job_fn, int dd_job_size, + void (*release)(struct device *), + struct module *dev_module); void bsg_job_put(struct bsg_job *job); int __must_check bsg_job_get(struct bsg_job *job); diff --git a/include/linux/bsg.h b/include/linux/bsg.h index 0c7dd9c..0e7c26c 100644 --- a/include/linux/bsg.h +++ b/include/linux/bsg.h @@ -23,11 +23,16 @@ struct bsg_class_device { struct kref ref; const struct bsg_ops *ops; void (*release)(struct device *); + struct module *parent_module; }; int bsg_register_queue(struct request_queue *q, struct device *parent, const char *name, const struct bsg_ops *ops, void (*release)(struct device *)); +int bsg_register_queue_ex(struct request_queue *q, struct device *parent, + const char *name, const struct bsg_ops *ops, + void (*release)(struct device *), + struct module *parent_module); int bsg_scsi_register_queue(struct request_queue *q, struct device *parent); void bsg_unregister_queue(struct request_queue *q); #else