From: viro@parcelfarce.linux.theplanet.co.uk raw.c has a refcounting bug (patch attached). As for the theory... If you have a pathname - use filp_open() or open_bdev_excl() and be done with that. bdget() et.al. are OK only if you really have nothing better than device number and that's a situation that should be avoided unless you really have no choice. Said that, we have the following primitives: * lookup_bdev(): takes a pathname, returns a reference to block_device. * bdget(): takes a number, returns a reference to block_device. * blkdev_get(): takes a reference to block_device and opens it. If open fails - drops the reference to block_device passed to it. * blkdev_put(): takes a reference to block_device and closes it. The reference is dropped. * bdput(): drops a reference to block_device. Note that behaviour of blkdev_get() and blkdev_put() is such that it makes for minimal cleanup code. bd_claim()/bd_release() is the exclusion mechanism - that's what mount, swapon, open with O_EXCL, etc. are using to avoid stepping on each others toes. bd_claim() claims bdev for given owner; if it's already owned and not by the same owner you'll get -EBUSY. bd_release() reverts the effect of bd_claim(). Note that if you claim the thing N times (with the same owner, obviously), you'll need N bd_release() before it stops being owned. raw.c grabbed a reference to bdev only after blkdev_get(). If blkdev_get() failed (e.g. media being absent), you've got an unbalanced bdput(). --- drivers/char/raw.c | 46 ++++++++++++++++++++++++---------------------- 1 files changed, 24 insertions(+), 22 deletions(-) diff -puN drivers/char/raw.c~raw-driver-refcounting-fix drivers/char/raw.c --- 25/drivers/char/raw.c~raw-driver-refcounting-fix 2004-01-11 22:06:16.000000000 -0800 +++ 25-akpm/drivers/char/raw.c 2004-01-11 22:06:16.000000000 -0800 @@ -57,29 +57,31 @@ static int raw_open(struct inode *inode, */ bdev = raw_devices[minor].binding; err = -ENODEV; - if (bdev) { - err = blkdev_get(bdev, filp->f_mode, 0, BDEV_RAW); - if (err) - goto out; - igrab(bdev->bd_inode); - err = bd_claim(bdev, raw_open); - if (err) { - blkdev_put(bdev, BDEV_RAW); - goto out; - } - err = set_blocksize(bdev, bdev_hardsect_size(bdev)); - if (err) { - bd_release(bdev); - blkdev_put(bdev, BDEV_RAW); - goto out; - } - filp->f_flags |= O_DIRECT; - filp->f_mapping = bdev->bd_inode->i_mapping; - if (++raw_devices[minor].inuse == 1) - filp->f_dentry->d_inode->i_mapping = - bdev->bd_inode->i_mapping; - } + if (!bdev) + goto out; + igrab(bdev->bd_inode); + err = blkdev_get(bdev, filp->f_mode, 0, BDEV_RAW); + if (err) + goto out; + err = bd_claim(bdev, raw_open); + if (err) + goto out1; + err = set_blocksize(bdev, bdev_hardsect_size(bdev)); + if (err) + goto out2; + filp->f_flags |= O_DIRECT; + filp->f_mapping = bdev->bd_inode->i_mapping; + if (++raw_devices[minor].inuse == 1) + filp->f_dentry->d_inode->i_mapping = + bdev->bd_inode->i_mapping; filp->private_data = bdev; + up(&raw_mutex); + return 0; + +out2: + bd_release(bdev); +out1: + blkdev_put(bdev, BDEV_RAW); out: up(&raw_mutex); return err; _