Re: [PATCH] bsg referencing bus driver module

From: Anatoliy Glagolev
Date: Tue May 01 2018 - 13:24:26 EST


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@xxxxxxxxx>
> Date: Wed, 25 Apr 2018 19:16:10 -0600
> Subject: [PATCH] bsg referencing parent module
> Signed-off-by: Anatoliy Glagolev <glagolig@xxxxxxxxx>
>
> 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
>