Received: by 10.192.165.148 with SMTP id m20csp21311imm; Thu, 19 Apr 2018 15:12:49 -0700 (PDT) X-Google-Smtp-Source: AIpwx4+K+Vh/EXMpq7oAj4ZrtHVb2afc1wv3hTSxq6z6gNkq4Oz4AN+RT4TOUQyYXumF3djf3iZr X-Received: by 2002:a17:902:2ac3:: with SMTP id j61-v6mr7511348plb.224.1524175969216; Thu, 19 Apr 2018 15:12:49 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1524175969; cv=none; d=google.com; s=arc-20160816; b=RrwJtAFR9pIfLjgh9fqcz/71YnBJaeAL6zneZ0pDcemgocoOqqoDGMnEA6dJ2hzUUi 6jrpQp2VBKB2q78ydpDYTBETJhs5mdUXfMfsExPOTiEznseGy7X0sTFuS12//5Wgu34W RRDlu9Qibn2ZSNAZ2qiOecemCKSuzhAbtmzHK5O73HY9oYFlAM2pz6bGqIRaCUcIvABS b/jtdLmaarD9bQ1SbrRpNjvaHCrfW4THG9MVrWMMNnoXHdKcL+gXOYYvS3nmARV6AKxI qhBcVptPoPAxCldgWQQxwEJZoGsYMqydlbsCfZQXAR0BoFPhQLMQUajXzUreK8D1gSUz gGDw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :references:in-reply-to:mime-version:dkim-signature :arc-authentication-results; bh=0YERWoP38dLM6+v9K1P+HxndSV6JYo/6tabPW/J3ojE=; b=nRmipPIPTnuWpcCKw+Jw4a3nipaqu2R1ZlTasDr4IOISWdjsieGL5jsohNZh1C61IQ 2OSWQTN8vwOZc2Sh99cuWa9a6XJD1PLUnB881HS7HPtYfLhQRNy/LYyvIWcOWKVi0X7D J5N9Q0V2csoA5kq+k2MQHsmJa6nuNz3SmRFAH3T1pdO4j+l8SZOamKqD4vZvnOJ2BGcN +2xvmtEPV7FnnCtZrQegGFdm+yYOYL3m6lrAFi/2ls9rR6AJoyjegN/+5S0tGLodzD7s h+hiKZzD7Kkl3GX1lZI/37WT5mcQWPzKXYOcZ+M6TqGmQVu7+UDCUsHfk9OGwQD5LMge Y9wQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=gzEqysRd; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id n14si3691977pgc.366.2018.04.19.15.11.59; Thu, 19 Apr 2018 15:12:49 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=gzEqysRd; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753535AbeDSWKM (ORCPT + 99 others); Thu, 19 Apr 2018 18:10:12 -0400 Received: from mail-qt0-f195.google.com ([209.85.216.195]:38921 "EHLO mail-qt0-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753462AbeDSWKK (ORCPT ); Thu, 19 Apr 2018 18:10:10 -0400 Received: by mail-qt0-f195.google.com with SMTP id l8-v6so7526227qtp.6; Thu, 19 Apr 2018 15:10:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=0YERWoP38dLM6+v9K1P+HxndSV6JYo/6tabPW/J3ojE=; b=gzEqysRdW0UbRbx9vXMdLX2ct0lS2FmRwikNswQx+RZEYcK5+KoULig9mOz+MUz258 C1xy20TxQJ7ldTw6g7nfuroHelJNWL8m9gpP2ecB0r/aHhwXQdDaF5EiAT2iHYQZM6Aa AhRQDNq0JUOrFqK4G2kQjyajCOCvxXUo22MIpRZfr8OvMMZwJQKf6bl3JTfWxAM3bA4r pEzqtSXTQDj1hDR+acoaTF56t/ysD+hFRpGZVyWf5WgVZhdaY8OItlJyQL4di+8RdoSG msXPWBTSCdzJp0St1YXQliyiJ058Gr7mTUUxs14xHBhm9vWBtcWxm8vWRQ4/5TFOD5g1 yTww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=0YERWoP38dLM6+v9K1P+HxndSV6JYo/6tabPW/J3ojE=; b=QjyjfadBfBO3wCMow2oajJ5OAUhGba/6quqtZRORp5naEyctUQUOzGhqPHClOgASOp RTBgt+EhqEEqC/0taJMQmrPKniMnrxxr1U64SeN55SO6FoXKBBhEGAfjp4uYIHdqOTVh Vurds+ocefh18sr46ORMERwIl+y0RPf1rE8pLCJN4a1xZtTU9uBOvSQs9bm6aA5yP4xr s1JaNjPpxDWhmPpJYr2KPluNoxbph0oRpPByf37DZx0PEZe46f++unNLs4WY8GxrXgPt iqIpClV3QKNz6dXCl07sxm0EuISjsSVV+lGc2AieACuBUp33wzPScfWJPJTAJkyz2Rzj 01sg== X-Gm-Message-State: ALQs6tDB1DGQzJMN5fJ4eRM59nGyD34WR+/f6adScM9L9w5gu/24Wx54 gOd8Uyd7NmobiZvD6iRaTIuRc/7eMeJWOZ2elbZz3Ncg X-Received: by 2002:ac8:90b:: with SMTP id t11-v6mr8104980qth.107.1524175809453; Thu, 19 Apr 2018 15:10:09 -0700 (PDT) MIME-Version: 1.0 Received: by 10.12.219.130 with HTTP; Thu, 19 Apr 2018 15:10:09 -0700 (PDT) In-Reply-To: References: From: Anatoliy Glagolev Date: Thu, 19 Apr 2018 15:10:09 -0700 Message-ID: Subject: Re: [PATCH] bsg referencing bus driver module To: linux-scsi@vger.kernel.org, linux-block@vger.kernel.org Cc: axboe@kernel.dk, fujita.tomonori@lab.ntt.co.jp, martin.petersen@oracle.com, jthumshirn@suse.de, hare@suse.com, bblock@linux.vnet.ibm.com, linux-kernel@vger.kernel.org Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Updated: rebased on recent Linux, cc-ed maintainers per instructions in MAINTAINERS file From df939b80d02bf37b21efaaef8ede86cfd39b0cb8 Mon Sep 17 00:00:00 2001 From: Anatoliy Glagolev Date: Thu, 19 Apr 2018 15:06:06 -0600 Subject: [PATCH] bsg referencing parent module 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 | 29 ++++++++++++++++++++++++++--- drivers/scsi/scsi_transport_fc.c | 8 ++++++-- include/linux/bsg-lib.h | 4 ++++ include/linux/bsg.h | 5 +++++ 5 files changed, 63 insertions(+), 7 deletions(-) diff --git a/block/bsg-lib.c b/block/bsg-lib.c index fc2e5ff..90c28fd 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 - 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..6920c5b 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -750,7 +750,8 @@ static struct bsg_device *__bsg_get_device(int minor, struct request_queue *q) 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; @@ -766,6 +767,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) @@ -781,22 +783,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 __poll_t bsg_poll(struct file *file, poll_table *wait) @@ -922,6 +936,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 +980,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 On Thu, Apr 19, 2018 at 10:07 AM, Anatoliy Glagolev wrote: > +linux-block > > On Tue, Apr 17, 2018 at 1:05 PM, Anatoliy Glagolev wrote: >> 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] [] scsi_host_dev_release+0x3f/0x130 >> [16834.647740,07] [] device_release+0x32/0xa0 >> [16834.653423,07] [] kobject_cleanup+0x77/0x190 >> [16834.659002,07] [] kobject_put+0x25/0x50 >> [16834.664430,07] [] put_device+0x17/0x20 >> [16834.669740,07] [] bsg_kref_release_function+0x24/0x30 >> [16834.675007,07] [] bsg_release+0x166/0x1d0 >> [16834.680148,07] [] __fput+0xcb/0x1d0 >> [16834.685156,07] [] ____fput+0xe/0x10 >> [16834.690017,07] [] task_work_run+0x86/0xb0 >> [16834.694781,07] [] exit_to_usermode_loop+0x6b/0x9a >> [16834.699466,07] [] syscall_return_slowpath+0x55/0x60 >> [16834.704110,07] [] 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