From patchwork Fri Oct 15 15:20:43 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Maxim Levitsky X-Patchwork-Id: 67972 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from canuck.infradead.org (canuck.infradead.org [134.117.69.58]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id CC60AB70E7 for ; Sat, 16 Oct 2010 02:27:58 +1100 (EST) Received: from localhost ([127.0.0.1] helo=canuck.infradead.org) by canuck.infradead.org with esmtp (Exim 4.72 #1 (Red Hat Linux)) id 1P6m5x-0000xT-N4; Fri, 15 Oct 2010 15:21:21 +0000 Received: from mail-bw0-f49.google.com ([209.85.214.49]) by canuck.infradead.org with esmtp (Exim 4.72 #1 (Red Hat Linux)) id 1P6m5r-0000we-Nd for linux-mtd@lists.infradead.org; Fri, 15 Oct 2010 15:21:17 +0000 Received: by mail-bw0-f49.google.com with SMTP id 7so1638853bwz.36 for ; Fri, 15 Oct 2010 08:21:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:from:to:cc:subject:date :message-id:x-mailer:in-reply-to:references; bh=5vF3lviz586OAyv5znq7a7lSDjq8gBBuO5Pcfr4/kQo=; b=aOtPftpuP1bNR2x0ACZSce4TX32rhHqnmpfcLseVtR6wtLjqcwH0JcRBl5eASfiQFo bRSUpqW33XW3NobMV6gpqAvVMDEcXMMHKMCJEn4G5mdHEYxDjYQfK6wMtnBIxppcX/5H afdixCujF8DIrGkx8Whxpkrqa0toUE2jdsZlI= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; b=Qiwq7oRDwruqdqAeMLAUIPqaj+HRCFHc8Oj0/f2USGzH32Diin2ssdFXNQCbWYXH5p lQakxzAniaIZl70/eZG3+fw3/uPVo2mEkMl/h+DyKl8Ga5yZ8APU6hUVkjYUx9i1shZ/ q1i1Ta6qRH2o3F0zAvr0X28kafWfhgq6jOImU= Received: by 10.204.76.80 with SMTP id b16mr809667bkk.160.1287156075260; Fri, 15 Oct 2010 08:21:15 -0700 (PDT) Received: from maxim-laptop ([77.125.87.13]) by mx.google.com with ESMTPS id o12sm11374046bkb.9.2010.10.15.08.21.06 (version=SSLv3 cipher=RC4-MD5); Fri, 15 Oct 2010 08:21:09 -0700 (PDT) From: Maxim Levitsky To: David Woodhouse Subject: [PATCH 1/5] MTD: blktrans: Allow to unload the mtdtrans module if its block devices aren't open Date: Fri, 15 Oct 2010 17:20:43 +0200 Message-Id: <1287156047-17439-2-git-send-email-maximlevitsky@gmail.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1287156047-17439-1-git-send-email-maximlevitsky@gmail.com> References: <1287156047-17439-1-git-send-email-maximlevitsky@gmail.com> X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20101015_112115_981017_8482CC04 X-CRM114-Status: GOOD ( 25.14 ) X-Spam-Score: -0.1 (/) X-Spam-Report: SpamAssassin version 3.3.1 on canuck.infradead.org summary: Content analysis details: (-0.1 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.214.49 listed in list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is freemail (maximlevitsky[at]gmail.com) -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature Cc: Andrew Morton , linux-mtd@lists.infradead.org, Maxim Levitsky , Maciej Rutecki X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-mtd-bounces@lists.infradead.org Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Now it once again possible to remove mtdtrans module. You still need to ensure that block devices of that module aren't mounted. This is due to the fact that as long as a block device is open, it still exists, therefore if we were to allow module removal, this block device might became used again. This time in addition to code review, I also made the code pass some torture tests like module reload in a loop + read in a loop + card insert/removal all at same time. The blktrans_open/blktrans_release don't take the mtd table lock because: While device is added (that includes execution of add_mtd_blktrans_dev) the lock is already taken Now suppose the device will never be removed. In this case even if we have changes in mtd table, the entry that we need will stay exactly the same. (Note that we don't look at table at all, just following private pointer of block device). Now suppose that someone tries to remove the mtd device. This will be propagated to trans driver which _ought_ to call del_mtd_blktrans_dev which will take the per device lock, release the mtd device and set trans->mtd = NULL. From this point on, following opens won't even be able to know anything about that mtd device (which at that point is likely not to exist) Also the same care is taken not to trip over NULL mtd pointer in blktrans_dev_release. Signed-off-by: Maxim Levitsky --- drivers/mtd/mtd_blkdevs.c | 52 ++++++++++++++++++++------------------------ 1 files changed, 24 insertions(+), 28 deletions(-) diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 62e6870..352831b 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -176,7 +176,7 @@ static void mtd_blktrans_request(struct request_queue *rq) static int blktrans_open(struct block_device *bdev, fmode_t mode) { struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); - int ret; + int ret = 0; if (!dev) return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/ @@ -184,17 +184,17 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode) lock_kernel(); mutex_lock(&dev->lock); - if (!dev->mtd) { - ret = -ENXIO; + if (dev->open++) goto unlock; - } - ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0; + kref_get(&dev->ref); + __module_get(dev->tr->owner); + + if (dev->mtd) { + ret = dev->tr->open ? dev->tr->open(dev) : 0; + __get_mtd_device(dev->mtd); + } - /* Take another reference on the device so it won't go away till - last release */ - if (!ret) - kref_get(&dev->ref); unlock: mutex_unlock(&dev->lock); blktrans_dev_put(dev); @@ -205,7 +205,7 @@ unlock: static int blktrans_release(struct gendisk *disk, fmode_t mode) { struct mtd_blktrans_dev *dev = blktrans_dev_get(disk); - int ret = -ENXIO; + int ret = 0; if (!dev) return ret; @@ -213,13 +213,16 @@ static int blktrans_release(struct gendisk *disk, fmode_t mode) lock_kernel(); mutex_lock(&dev->lock); - /* Release one reference, we sure its not the last one here*/ - kref_put(&dev->ref, blktrans_dev_release); - - if (!dev->mtd) + if (--dev->open) goto unlock; - ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0; + kref_put(&dev->ref, blktrans_dev_release); + module_put(dev->tr->owner); + + if (dev->mtd) { + ret = dev->tr->release ? dev->tr->release(dev) : 0; + __put_mtd_device(dev->mtd); + } unlock: mutex_unlock(&dev->lock); blktrans_dev_put(dev); @@ -385,9 +388,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) gd->queue = new->rq; - __get_mtd_device(new->mtd); - __module_get(tr->owner); - /* Create processing thread */ /* TODO: workqueue ? */ new->thread = kthread_run(mtd_blktrans_thread, new, @@ -410,8 +410,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) } return 0; error4: - module_put(tr->owner); - __put_mtd_device(new->mtd); blk_cleanup_queue(new->rq); error3: put_disk(new->disk); @@ -448,17 +446,15 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) blk_start_queue(old->rq); spin_unlock_irqrestore(&old->queue_lock, flags); - /* Ask trans driver for release to the mtd device */ + /* If the device is currently open, tell trans driver to close it, + then put mtd device, and don't touch it again */ mutex_lock(&old->lock); - if (old->open && old->tr->release) { - old->tr->release(old); - old->open = 0; + if (old->open) { + if (old->tr->release) + old->tr->release(old); + __put_mtd_device(old->mtd); } - __put_mtd_device(old->mtd); - module_put(old->tr->owner); - - /* At that point, we don't touch the mtd anymore */ old->mtd = NULL; mutex_unlock(&old->lock);