mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Peter Osterlund <petero2@telia.com>
To: linux-kernel@vger.kernel.org
Cc: Jens Axboe <axboe@suse.de>, Andrew Morton <akpm@osdl.org>
Subject: [PATCH] Fix open/close races in pktcdvd
Date: 04 Jul 2004 14:37:24 +0200	[thread overview]
Message-ID: <m24qoouewr.fsf@telia.com> (raw)
In-Reply-To: <m2lli36ec9.fsf@telia.com>

The handling of the pd->refcnt variable is racy in a number of
places. For example, running:

while true ; do usleep 10 ; pktsetup /dev/pktcdvd0 /dev/hdc ; done &
while true ; do pktsetup -d /dev/pktcdvd0 ; done

makes a pktsetup process get stuck in D state after a while.

This patch fixes it by introducing a mutex to protect the refcnt
variable and critical sections in the open/release/setup/tear-down
functions.

This patch applies cleanly on top of the "Fix race in pktcdvd kernel
thread handling" patch.


Signed-off-by: Peter Osterlund <petero2@telia.com>

---

 linux-petero/drivers/block/pktcdvd.c |   88 +++++++++++++++++++++--------------
 linux-petero/include/linux/pktcdvd.h |    3 -
 2 files changed, 55 insertions(+), 36 deletions(-)

diff -puN drivers/block/pktcdvd.c~packet-open-race-fix drivers/block/pktcdvd.c
--- linux/drivers/block/pktcdvd.c~packet-open-race-fix	2004-07-04 14:09:40.840062728 +0200
+++ linux-petero/drivers/block/pktcdvd.c	2004-07-04 14:09:40.850061208 +0200
@@ -1998,10 +1998,6 @@ static void pkt_release_dev(struct pktcd
 {
 	struct block_device *bdev;
 
-	atomic_dec(&pd->refcnt);
-	if (atomic_read(&pd->refcnt) > 0)
-		return;
-
 	bdev = bdget(pd->pkt_dev);
 	if (bdev) {
 		fsync_bdev(bdev);
@@ -2029,6 +2025,7 @@ static int pkt_open(struct inode *inode,
 	struct pktcdvd_device *pd = NULL;
 	struct block_device *pkt_bdev;
 	int ret;
+	int special_open, exclusive;
 
 	VPRINTK("pktcdvd: entering open\n");
 
@@ -2038,21 +2035,30 @@ static int pkt_open(struct inode *inode,
 		goto out;
 	}
 
+	special_open = 0;
+	if (((file->f_flags & O_ACCMODE) == O_RDONLY) && (file->f_flags & O_CREAT))
+		special_open = 1;
+
+	exclusive = 0;
+	if ((file->f_mode & FMODE_WRITE) || special_open)
+		exclusive = 1;
+
 	/*
 	 * either device is not configured, or pktsetup is old and doesn't
 	 * use O_CREAT to create device
 	 */
 	pd = &pkt_devs[iminor(inode)];
-	if (!pd->dev && !(file->f_flags & O_CREAT)) {
+	if (!pd->dev && !special_open) {
 		VPRINTK("pktcdvd: not configured and O_CREAT not set\n");
 		ret = -ENXIO;
 		goto out;
 	}
 
-	atomic_inc(&pd->refcnt);
-	if (atomic_read(&pd->refcnt) > 1) {
-		if (file->f_mode & FMODE_WRITE) {
-			VPRINTK("pktcdvd: busy open for write\n");
+	down(&pd->ctl_mutex);
+	pd->refcnt++;
+	if (pd->refcnt > 1) {
+		if (exclusive) {
+			VPRINTK("pktcdvd: busy open\n");
 			ret = -EBUSY;
 			goto out_dec;
 		}
@@ -2060,10 +2066,10 @@ static int pkt_open(struct inode *inode,
 		/*
 		 * Not first open, everything is already set up
 		 */
-		return 0;
+		goto done;
 	}
 
-	if (((file->f_flags & O_ACCMODE) != O_RDONLY) || !(file->f_flags & O_CREAT)) {
+	if (!special_open) {
 		if (pkt_open_dev(pd, file->f_mode & FMODE_WRITE)) {
 			ret = -EIO;
 			goto out_dec;
@@ -2079,16 +2085,20 @@ static int pkt_open(struct inode *inode,
 		set_blocksize(pkt_bdev, CD_FRAMESIZE);
 		bdput(pkt_bdev);
 	}
+
+done:
+	up(&pd->ctl_mutex);
 	return 0;
 
 out_dec:
-	atomic_dec(&pd->refcnt);
-	if (atomic_read(&pd->refcnt) == 0) {
+	pd->refcnt--;
+	if (pd->refcnt == 0) {
 		if (pd->bdev) {
 			blkdev_put(pd->bdev);
 			pd->bdev = NULL;
 		}
 	}
+	up(&pd->ctl_mutex);
 out:
 	VPRINTK("pktcdvd: failed open (%d)\n", ret);
 	return ret;
@@ -2099,11 +2109,17 @@ static int pkt_close(struct inode *inode
 	struct pktcdvd_device *pd = &pkt_devs[iminor(inode)];
 	int ret = 0;
 
+	down(&pd->ctl_mutex);
+	pd->refcnt--;
+	BUG_ON(pd->refcnt < 0);
+	if (pd->refcnt > 0)
+		goto out;
 	if (pd->dev) {
 		int flush = test_bit(PACKET_WRITABLE, &pd->flags);
 		pkt_release_dev(pd, flush);
 	}
-
+out:
+	up(&pd->ctl_mutex);
 	return ret;
 }
 
@@ -2332,7 +2348,7 @@ static int pkt_proc_device(struct pktcdv
 	b += sprintf(b, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1);
 
 	b += sprintf(b, "\nMisc:\n");
-	b += sprintf(b, "\treference count:\t%d\n", atomic_read(&pd->refcnt));
+	b += sprintf(b, "\treference count:\t%d\n", pd->refcnt);
 	b += sprintf(b, "\tflags:\t\t\t0x%lx\n", pd->flags);
 	b += sprintf(b, "\tread speed:\t\t%ukB/s\n", pd->read_speed * 150);
 	b += sprintf(b, "\twrite speed:\t\t%ukB/s\n", pd->write_speed * 150);
@@ -2371,8 +2387,6 @@ static int pkt_read_proc(char *page, cha
 	return len;
 }
 
-extern struct block_device_operations pktcdvd_ops;
-
 static int pkt_new_dev(struct pktcdvd_device *pd, struct block_device *bdev)
 {
 	dev_t dev = bdev->bd_dev;
@@ -2392,10 +2406,8 @@ static int pkt_new_dev(struct pktcdvd_de
 	/* This is safe, since we have a reference from open(). */
 	__module_get(THIS_MODULE);
 
-	memset(pd, 0, sizeof(struct pktcdvd_device));
+	memset(&pd->stats, 0, sizeof(struct packet_stats));
 
-	spin_lock_init(&pd->lock);
-	spin_lock_init(&pd->iosched.lock);
 	if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {
 		printk("pktcdvd: not enough memory for buffers\n");
 		ret = -ENOMEM;
@@ -2404,12 +2416,7 @@ static int pkt_new_dev(struct pktcdvd_de
 
 	set_blocksize(bdev, CD_FRAMESIZE);
 	pd->dev = dev;
-	pd->bdev = NULL;
-	pd->pkt_dev = MKDEV(PACKET_MAJOR, minor);
-	sprintf(pd->name, "pktcdvd%d", minor);
-	atomic_set(&pd->refcnt, 0);
-	init_waitqueue_head(&pd->wqueue);
-	init_MUTEX_LOCKED(&pd->cdrw.thr_sem);
+	BUG_ON(pd->bdev);
 
 	pkt_init_queue(pd);
 
@@ -2428,7 +2435,6 @@ static int pkt_new_dev(struct pktcdvd_de
 
 out_thread:
 	pkt_shrink_pktlist(pd);
-	memset(pd, 0, sizeof(*pd));
 out_mem:
 	/* This is safe: open() is still holding a reference. */
 	module_put(THIS_MODULE);
@@ -2464,10 +2470,7 @@ static int pkt_setup_dev(struct pktcdvd_
 		printk("pktcdvd: Can't write to read-only dev\n");
 		goto out;
 	}
-	if ((ret = pkt_new_dev(pd, inode->i_bdev)))
-		goto out;
-
-	atomic_inc(&pd->refcnt);
+	ret = pkt_new_dev(pd, inode->i_bdev);
 
 out:
 	fput(file);
@@ -2502,8 +2505,8 @@ static int pkt_remove_dev(struct pktcdvd
 	pkt_shrink_pktlist(pd);
 
 	remove_proc_entry(pd->name, pkt_proc);
+	pd->dev = 0;
 	DPRINTK("pktcdvd: writer %d unmapped\n", pkt_get_minor(pd));
-	memset(pd, 0, sizeof(struct pktcdvd_device));
 	/* This is safe: open() is still holding a reference. */
 	module_put(THIS_MODULE);
 	return 0;
@@ -2527,6 +2530,7 @@ static int pkt_media_changed(struct gend
 static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct pktcdvd_device *pd = &pkt_devs[iminor(inode)];
+	int ret;
 
 	VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode));
 
@@ -2553,9 +2557,14 @@ static int pkt_ioctl(struct inode *inode
 	case PACKET_TEARDOWN_DEV:
 		if (!capable(CAP_SYS_ADMIN))
 			return -EPERM;
-		if (atomic_read(&pd->refcnt) != 1)
-			return -EBUSY;
-		return pkt_remove_dev(pd);
+		down(&pd->ctl_mutex);
+		BUG_ON(pd->refcnt < 1);
+		if (pd->refcnt != 1)
+			ret = -EBUSY;
+		else
+			ret = pkt_remove_dev(pd);
+		up(&pd->ctl_mutex);
+		return ret;
 
 	case BLKROSET:
 		if (capable(CAP_SYS_ADMIN))
@@ -2629,6 +2638,15 @@ int pkt_init(void)
 	for (i = 0; i < MAX_WRITERS; i++) {
 		struct pktcdvd_device *pd = &pkt_devs[i];
 		struct gendisk *disk = disks[i];
+
+		spin_lock_init(&pd->lock);
+		spin_lock_init(&pd->iosched.lock);
+		pd->pkt_dev = MKDEV(PACKET_MAJOR, i);
+		sprintf(pd->name, "pktcdvd%d", i);
+		init_waitqueue_head(&pd->wqueue);
+		init_MUTEX_LOCKED(&pd->cdrw.thr_sem);
+		init_MUTEX(&pd->ctl_mutex);
+
 		disk->major = PACKET_MAJOR;
 		disk->first_minor = i;
 		disk->fops = &pktcdvd_ops;
diff -puN include/linux/pktcdvd.h~packet-open-race-fix include/linux/pktcdvd.h
--- linux/include/linux/pktcdvd.h~packet-open-race-fix	2004-07-04 14:09:40.843062272 +0200
+++ linux-petero/include/linux/pktcdvd.h	2004-07-04 14:09:40.850061208 +0200
@@ -236,7 +236,8 @@ struct pktcdvd_device
 	char			name[20];
 	struct packet_settings	settings;
 	struct packet_stats	stats;
-	atomic_t		refcnt;
+	struct semaphore	ctl_mutex;	/* Serialize access to refcnt */
+	int			refcnt;		/* Open count */
 	__u8			write_speed;	/* current write speed */
 	__u8			read_speed;	/* current read speed */
 	unsigned long		offset;		/* start offset */
_

-- 
Peter Osterlund - petero2@telia.com
http://w1.894.telia.com/~u89404340

  parent reply	other threads:[~2004-07-04 12:41 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2004-07-01 13:34 [PATCH] CDRW packet writing support for 2.6.7-bk13 Peter Osterlund
2004-07-02 21:52 ` Peter Osterlund
2004-07-02 22:08   ` Andrew Morton
2004-07-02 22:47     ` Greg KH
2004-07-02 22:59       ` Andrew Morton
2004-07-02 23:24         ` Peter Osterlund
2004-07-02 23:51           ` Andrew Morton
2004-07-04 11:57             ` Peter Osterlund
2004-07-04 20:58               ` Andrew Morton
2004-07-04 21:06                 ` Christoph Hellwig
2004-07-04 12:30 ` [PATCH] Fix race in pktcdvd kernel thread handling Peter Osterlund
2004-07-04 12:37 ` Peter Osterlund [this message]
2004-07-04 13:05 ` [PATCH] CDRW packet writing support for 2.6.7-bk13 Christoph Hellwig
2004-07-04 23:49   ` Peter Osterlund
2004-07-05  0:01     ` Peter Osterlund
2004-07-10 23:20       ` Arnd Bergmann
2004-07-10 23:27         ` Christoph Hellwig
2004-07-11  1:06           ` Peter Osterlund
2004-07-12 16:25             ` Arnd Bergmann
2004-07-12 16:34               ` Christoph Hellwig
2004-07-13  6:04                 ` Jens Axboe
2004-07-14  0:06             ` [RFC][PATCH] Control pktcdvd with an auxiliary character device Peter Osterlund
2004-07-14  0:17               ` Peter Osterlund
2004-07-05  8:17     ` [PATCH] CDRW packet writing support for 2.6.7-bk13 Jens Axboe
2004-07-05 10:26     ` Christoph Hellwig
2004-07-06  8:45     ` Peter Osterlund
2004-07-07 10:06   ` Peter Osterlund

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=m24qoouewr.fsf@telia.com \
    --to=petero2@telia.com \
    --cc=akpm@osdl.org \
    --cc=axboe@suse.de \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome