Skip to content

Commit

Permalink
[SCSI] bsg: add release callback support
Browse files Browse the repository at this point in the history
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 <fujita.tomonori@lab.ntt.co.jp>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
  • Loading branch information
fujita authored and James Bottomley committed Apr 22, 2008
1 parent 643eb2d commit 97f46ae
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 20 deletions.
43 changes: 29 additions & 14 deletions block/bsg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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)
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion drivers/scsi/scsi_sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
goto out;
}

error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL);
error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL, NULL);

if (error)
sdev_printk(KERN_INFO, sdev,
Expand Down
2 changes: 1 addition & 1 deletion drivers/scsi/scsi_transport_sas.c
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy)
if (!q)
return -ENOMEM;

error = bsg_register_queue(q, dev, name);
error = bsg_register_queue(q, dev, name, NULL);
if (error) {
blk_cleanup_queue(q);
return -ENOMEM;
Expand Down
14 changes: 10 additions & 4 deletions include/linux/bsg.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,25 @@ struct sg_io_v4 {
#if defined(CONFIG_BLK_DEV_BSG)
struct bsg_class_device {
struct device *class_dev;
struct device *dev;
struct device *parent;
int minor;
struct request_queue *queue;
struct kref ref;
void (*release)(struct device *);
};

extern int bsg_register_queue(struct request_queue *, struct device *, const char *);
extern int bsg_register_queue(struct request_queue *q,
struct device *parent, const char *name,
void (*release)(struct device *));
extern void bsg_unregister_queue(struct request_queue *);
#else
static inline int bsg_register_queue(struct request_queue * rq, struct device *dev, const char *name)
static inline int bsg_register_queue(struct request_queue *q,
struct device *parent, const char *name,
void (*release)(struct device *))
{
return 0;
}
static inline void bsg_unregister_queue(struct request_queue *rq)
static inline void bsg_unregister_queue(struct request_queue *q)
{
}
#endif
Expand Down

0 comments on commit 97f46ae

Please sign in to comment.