From d45ac4fa8f277e1ec5acfb67ce5d6406555760cf Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 31 Mar 2008 10:03:38 +0900 Subject: [SCSI] bsg: takes a ref to struct device in fops->open bsg_register_queue() takes a ref to struct device that a caller passes. For example, bsg takes a ref to the sdev_gendev for scsi devices. However, bsg doesn't inrease the refcount in fops->open. So while an application opens a bsg device, the scsi device that the bsg device holds can go away (bsg also takes a ref to a queue, but it doesn't prevent the device from going away). With this patch, bsg increases the refcount of struct device in fops->open and decreases it in fops->release. Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- block/bsg.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'block/bsg.c') diff --git a/block/bsg.c b/block/bsg.c index 8917c5174dc2..d8e0cb8dd6be 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -705,6 +705,7 @@ static struct bsg_device *bsg_alloc_device(void) static int bsg_put_device(struct bsg_device *bd) { int ret = 0; + struct device *dev = bd->queue->bsg_dev.dev; mutex_lock(&bsg_mutex); @@ -730,6 +731,7 @@ static int bsg_put_device(struct bsg_device *bd) kfree(bd); out: mutex_unlock(&bsg_mutex); + put_device(dev); return ret; } @@ -789,21 +791,27 @@ static struct bsg_device *bsg_get_device(struct inode *inode, struct file *file) struct bsg_device *bd; struct bsg_class_device *bcd; - bd = __bsg_get_device(iminor(inode)); - if (bd) - return bd; - /* * find the class device */ mutex_lock(&bsg_mutex); bcd = idr_find(&bsg_minor_idr, iminor(inode)); + if (bcd) + get_device(bcd->dev); mutex_unlock(&bsg_mutex); if (!bcd) return ERR_PTR(-ENODEV); - return bsg_add_device(inode, bcd->queue, file); + bd = __bsg_get_device(iminor(inode)); + if (bd) + return bd; + + bd = bsg_add_device(inode, bcd->queue, file); + if (IS_ERR(bd)) + put_device(bcd->dev); + + return bd; } static int bsg_open(struct inode *inode, struct file *file) @@ -942,7 +950,6 @@ void bsg_unregister_queue(struct request_queue *q) class_device_unregister(bcd->class_dev); put_device(bcd->dev); bcd->class_dev = NULL; - bcd->dev = NULL; mutex_unlock(&bsg_mutex); } EXPORT_SYMBOL_GPL(bsg_unregister_queue); -- cgit v1.2.3 From c3ff1b90d8924dd1c55c3b56a79bfc563ace4a42 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 31 Mar 2008 10:03:39 +0900 Subject: [SCSI] bsg: replace kobject_get with blk_get_queue Both takes a ref to a queue. But blk_get_queue checks QUEUE_FLAG_DEAD and is more appropriate interface here. Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- block/bsg.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'block/bsg.c') diff --git a/block/bsg.c b/block/bsg.c index d8e0cb8dd6be..e2c65a150a79 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -740,16 +740,21 @@ static struct bsg_device *bsg_add_device(struct inode *inode, struct file *file) { struct bsg_device *bd; + int ret; #ifdef BSG_DEBUG unsigned char buf[32]; #endif + ret = blk_get_queue(rq); + if (ret) + return ERR_PTR(-ENXIO); bd = bsg_alloc_device(); - if (!bd) + if (!bd) { + blk_put_queue(rq); return ERR_PTR(-ENOMEM); + } bd->queue = rq; - kobject_get(&rq->kobj); bsg_set_block(bd, file); atomic_set(&bd->ref_count, 1); -- cgit v1.2.3 From 43ac9e62c4a0a47fe3de1f1eb9ca7b8c91dce234 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 31 Mar 2008 10:03:40 +0900 Subject: [SCSI] bsg: use better helper list functions This replace hlist_for_each and list_entry with hlist_for_each_entry and list_first_entry respectively. Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- block/bsg.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'block/bsg.c') diff --git a/block/bsg.c b/block/bsg.c index e2c65a150a79..b413318a7c5d 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -368,7 +368,7 @@ static struct bsg_command *bsg_next_done_cmd(struct bsg_device *bd) spin_lock_irq(&bd->lock); if (bd->done_cmds) { - bc = list_entry(bd->done_list.next, struct bsg_command, list); + bc = list_first_entry(&bd->done_list, struct bsg_command, list); list_del(&bc->list); bd->done_cmds--; } @@ -772,21 +772,19 @@ static struct bsg_device *bsg_add_device(struct inode *inode, static struct bsg_device *__bsg_get_device(int minor) { - struct bsg_device *bd = NULL; + struct bsg_device *bd; struct hlist_node *entry; mutex_lock(&bsg_mutex); - hlist_for_each(entry, bsg_dev_idx_hash(minor)) { - bd = hlist_entry(entry, struct bsg_device, dev_list); + hlist_for_each_entry(bd, entry, bsg_dev_idx_hash(minor), dev_list) { if (bd->minor == minor) { atomic_inc(&bd->ref_count); - break; + goto found; } - - bd = NULL; } - + bd = NULL; +found: mutex_unlock(&bsg_mutex); return bd; } -- cgit v1.2.3 From 842ea771c38a3f0f78bdb1b4d47881e6a210fc15 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 31 Mar 2008 10:03:41 +0900 Subject: [SCSI] bsg: remove minor in struct bsg_device minor in struct bsg_device is used as identifier to find the corresponding struct bsg_device_class. However, request_queuse can be used as identifier for that and the minor in struct bsg_device is unnecessary. Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- block/bsg.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'block/bsg.c') diff --git a/block/bsg.c b/block/bsg.c index b413318a7c5d..933998124211 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -37,7 +37,6 @@ struct bsg_device { struct list_head done_list; struct hlist_node dev_list; atomic_t ref_count; - int minor; int queued_cmds; int done_cmds; wait_queue_head_t wq_done; @@ -758,9 +757,8 @@ static struct bsg_device *bsg_add_device(struct inode *inode, bsg_set_block(bd, file); atomic_set(&bd->ref_count, 1); - bd->minor = iminor(inode); mutex_lock(&bsg_mutex); - hlist_add_head(&bd->dev_list, bsg_dev_idx_hash(bd->minor)); + hlist_add_head(&bd->dev_list, bsg_dev_idx_hash(iminor(inode))); strncpy(bd->name, rq->bsg_dev.class_dev->class_id, sizeof(bd->name) - 1); dprintk("bound to <%s>, max queue %d\n", @@ -770,7 +768,7 @@ static struct bsg_device *bsg_add_device(struct inode *inode, return bd; } -static struct bsg_device *__bsg_get_device(int minor) +static struct bsg_device *__bsg_get_device(int minor, struct request_queue *q) { struct bsg_device *bd; struct hlist_node *entry; @@ -778,7 +776,7 @@ static struct bsg_device *__bsg_get_device(int minor) mutex_lock(&bsg_mutex); hlist_for_each_entry(bd, entry, bsg_dev_idx_hash(minor), dev_list) { - if (bd->minor == minor) { + if (bd->queue == q) { atomic_inc(&bd->ref_count); goto found; } @@ -806,7 +804,7 @@ static struct bsg_device *bsg_get_device(struct inode *inode, struct file *file) if (!bcd) return ERR_PTR(-ENODEV); - bd = __bsg_get_device(iminor(inode)); + bd = __bsg_get_device(iminor(inode), bcd->queue); if (bd) return bd; -- cgit v1.2.3 From 99773aab0377ee5bcaf37b7cd2577c3465422dab Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 31 Mar 2008 10:03:42 +0900 Subject: [SCSI] bsg: no need to set BSG_F_BLOCK bit in bsg_complete_all_commands Before bsg_complete_all_commands is called, BSG_F_BLOCK bit is always set. Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- block/bsg.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'block/bsg.c') diff --git a/block/bsg.c b/block/bsg.c index 933998124211..302ac1f5af39 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -467,8 +467,6 @@ static int bsg_complete_all_commands(struct bsg_device *bd) dprintk("%s: entered\n", bd->name); - set_bit(BSG_F_BLOCK, &bd->flags); - /* * wait for all commands to complete */ -- cgit v1.2.3 From ee959b00c335d7780136c5abda37809191fe52c3 Mon Sep 17 00:00:00 2001 From: Tony Jones Date: Fri, 22 Feb 2008 00:13:36 +0100 Subject: SCSI: convert struct class_device to struct device It's big, but there doesn't seem to be a way to split it up smaller... Signed-off-by: Tony Jones Signed-off-by: Kay Sievers Cc: Roland Dreier Cc: Sean Hefty Cc: Hal Rosenstock Cc: James Bottomley Signed-off-by: Greg Kroah-Hartman --- block/bsg.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'block/bsg.c') diff --git a/block/bsg.c b/block/bsg.c index 302ac1f5af39..f51172ed27c2 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -758,7 +758,7 @@ static struct bsg_device *bsg_add_device(struct inode *inode, mutex_lock(&bsg_mutex); hlist_add_head(&bd->dev_list, bsg_dev_idx_hash(iminor(inode))); - strncpy(bd->name, rq->bsg_dev.class_dev->class_id, sizeof(bd->name) - 1); + strncpy(bd->name, rq->bsg_dev.class_dev->bus_id, sizeof(bd->name) - 1); dprintk("bound to <%s>, max queue %d\n", format_dev_t(buf, inode->i_rdev), bd->max_queue); @@ -946,7 +946,7 @@ void bsg_unregister_queue(struct request_queue *q) mutex_lock(&bsg_mutex); idr_remove(&bsg_minor_idr, bcd->minor); sysfs_remove_link(&q->kobj, "bsg"); - class_device_unregister(bcd->class_dev); + device_unregister(bcd->class_dev); put_device(bcd->dev); bcd->class_dev = NULL; mutex_unlock(&bsg_mutex); @@ -959,7 +959,7 @@ int bsg_register_queue(struct request_queue *q, struct device *gdev, struct bsg_class_device *bcd; dev_t dev; int ret, minor; - struct class_device *class_dev = NULL; + struct device *class_dev = NULL; const char *devname; if (name) @@ -998,8 +998,7 @@ int bsg_register_queue(struct request_queue *q, struct device *gdev, bcd->queue = q; bcd->dev = get_device(gdev); dev = MKDEV(bsg_major, bcd->minor); - class_dev = class_device_create(bsg_class, NULL, dev, gdev, "%s", - devname); + class_dev = device_create(bsg_class, gdev, dev, "%s", devname); if (IS_ERR(class_dev)) { ret = PTR_ERR(class_dev); goto put_dev; @@ -1016,7 +1015,7 @@ int bsg_register_queue(struct request_queue *q, struct device *gdev, return 0; unregister_class_dev: - class_device_unregister(class_dev); + device_unregister(class_dev); put_dev: put_device(gdev); remove_idr: -- cgit v1.2.3 From 97f46ae45c70857e459b7f8df1fc2807e7bd90a9 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 19 Apr 2008 00:43:14 +0900 Subject: [SCSI] bsg: add release callback support This patch adds release callback support, which is called when a bsg device goes away. bsg_register_queue() takes a pointer to a callback function. This feature is useful for stuff like sas_host that can't use the release callback in struct device. If a caller doesn't need bsg's release callback, it can call bsg_register_queue() with NULL pointer (e.g. scsi devices can use release callback in struct device so they don't need bsg's callback). With this patch, bsg uses kref for refcounts on bsg devices instead of get/put_device in fops->open/release. bsg calls put_device and the caller's release callback (if it was registered) in kref_put's release. Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- block/bsg.c | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) (limited to 'block/bsg.c') diff --git a/block/bsg.c b/block/bsg.c index f51172ed27c2..23ea4fd1a66d 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -699,14 +699,26 @@ static struct bsg_device *bsg_alloc_device(void) return bd; } +static void bsg_kref_release_function(struct kref *kref) +{ + struct bsg_class_device *bcd = + container_of(kref, struct bsg_class_device, ref); + + if (bcd->release) + bcd->release(bcd->parent); + + put_device(bcd->parent); +} + static int bsg_put_device(struct bsg_device *bd) { - int ret = 0; - struct device *dev = bd->queue->bsg_dev.dev; + int ret = 0, do_free; + struct request_queue *q = bd->queue; mutex_lock(&bsg_mutex); - if (!atomic_dec_and_test(&bd->ref_count)) + do_free = atomic_dec_and_test(&bd->ref_count); + if (!do_free) goto out; dprintk("%s: tearing down\n", bd->name); @@ -723,12 +735,13 @@ static int bsg_put_device(struct bsg_device *bd) */ ret = bsg_complete_all_commands(bd); - blk_put_queue(bd->queue); hlist_del(&bd->dev_list); kfree(bd); out: mutex_unlock(&bsg_mutex); - put_device(dev); + kref_put(&q->bsg_dev.ref, bsg_kref_release_function); + if (do_free) + blk_put_queue(q); return ret; } @@ -796,7 +809,7 @@ static struct bsg_device *bsg_get_device(struct inode *inode, struct file *file) mutex_lock(&bsg_mutex); bcd = idr_find(&bsg_minor_idr, iminor(inode)); if (bcd) - get_device(bcd->dev); + kref_get(&bcd->ref); mutex_unlock(&bsg_mutex); if (!bcd) @@ -808,7 +821,7 @@ static struct bsg_device *bsg_get_device(struct inode *inode, struct file *file) bd = bsg_add_device(inode, bcd->queue, file); if (IS_ERR(bd)) - put_device(bcd->dev); + kref_put(&bcd->ref, bsg_kref_release_function); return bd; } @@ -947,14 +960,14 @@ void bsg_unregister_queue(struct request_queue *q) idr_remove(&bsg_minor_idr, bcd->minor); sysfs_remove_link(&q->kobj, "bsg"); device_unregister(bcd->class_dev); - put_device(bcd->dev); bcd->class_dev = NULL; + kref_put(&bcd->ref, bsg_kref_release_function); mutex_unlock(&bsg_mutex); } EXPORT_SYMBOL_GPL(bsg_unregister_queue); -int bsg_register_queue(struct request_queue *q, struct device *gdev, - const char *name) +int bsg_register_queue(struct request_queue *q, struct device *parent, + const char *name, void (*release)(struct device *)) { struct bsg_class_device *bcd; dev_t dev; @@ -965,7 +978,7 @@ int bsg_register_queue(struct request_queue *q, struct device *gdev, if (name) devname = name; else - devname = gdev->bus_id; + devname = parent->bus_id; /* * we need a proper transport to send commands, not a stacked device @@ -996,9 +1009,11 @@ int bsg_register_queue(struct request_queue *q, struct device *gdev, bcd->minor = minor; bcd->queue = q; - bcd->dev = get_device(gdev); + bcd->parent = get_device(parent); + bcd->release = release; + kref_init(&bcd->ref); dev = MKDEV(bsg_major, bcd->minor); - class_dev = device_create(bsg_class, gdev, dev, "%s", devname); + class_dev = device_create(bsg_class, parent, dev, "%s", devname); if (IS_ERR(class_dev)) { ret = PTR_ERR(class_dev); goto put_dev; @@ -1017,7 +1032,7 @@ int bsg_register_queue(struct request_queue *q, struct device *gdev, unregister_class_dev: device_unregister(class_dev); put_dev: - put_device(gdev); + put_device(parent); remove_idr: idr_remove(&bsg_minor_idr, minor); unlock: -- cgit v1.2.3