mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
[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 <fujita.tomonori@lab.ntt.co.jp> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
parent
643eb2d932
commit
97f46ae45c
4 changed files with 41 additions and 20 deletions
43
block/bsg.c
43
block/bsg.c
|
@ -699,14 +699,26 @@ static struct bsg_device *bsg_alloc_device(void)
|
||||||
return bd;
|
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)
|
static int bsg_put_device(struct bsg_device *bd)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0, do_free;
|
||||||
struct device *dev = bd->queue->bsg_dev.dev;
|
struct request_queue *q = bd->queue;
|
||||||
|
|
||||||
mutex_lock(&bsg_mutex);
|
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;
|
goto out;
|
||||||
|
|
||||||
dprintk("%s: tearing down\n", bd->name);
|
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);
|
ret = bsg_complete_all_commands(bd);
|
||||||
|
|
||||||
blk_put_queue(bd->queue);
|
|
||||||
hlist_del(&bd->dev_list);
|
hlist_del(&bd->dev_list);
|
||||||
kfree(bd);
|
kfree(bd);
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&bsg_mutex);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -796,7 +809,7 @@ static struct bsg_device *bsg_get_device(struct inode *inode, struct file *file)
|
||||||
mutex_lock(&bsg_mutex);
|
mutex_lock(&bsg_mutex);
|
||||||
bcd = idr_find(&bsg_minor_idr, iminor(inode));
|
bcd = idr_find(&bsg_minor_idr, iminor(inode));
|
||||||
if (bcd)
|
if (bcd)
|
||||||
get_device(bcd->dev);
|
kref_get(&bcd->ref);
|
||||||
mutex_unlock(&bsg_mutex);
|
mutex_unlock(&bsg_mutex);
|
||||||
|
|
||||||
if (!bcd)
|
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);
|
bd = bsg_add_device(inode, bcd->queue, file);
|
||||||
if (IS_ERR(bd))
|
if (IS_ERR(bd))
|
||||||
put_device(bcd->dev);
|
kref_put(&bcd->ref, bsg_kref_release_function);
|
||||||
|
|
||||||
return bd;
|
return bd;
|
||||||
}
|
}
|
||||||
|
@ -947,14 +960,14 @@ void bsg_unregister_queue(struct request_queue *q)
|
||||||
idr_remove(&bsg_minor_idr, bcd->minor);
|
idr_remove(&bsg_minor_idr, bcd->minor);
|
||||||
sysfs_remove_link(&q->kobj, "bsg");
|
sysfs_remove_link(&q->kobj, "bsg");
|
||||||
device_unregister(bcd->class_dev);
|
device_unregister(bcd->class_dev);
|
||||||
put_device(bcd->dev);
|
|
||||||
bcd->class_dev = NULL;
|
bcd->class_dev = NULL;
|
||||||
|
kref_put(&bcd->ref, bsg_kref_release_function);
|
||||||
mutex_unlock(&bsg_mutex);
|
mutex_unlock(&bsg_mutex);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(bsg_unregister_queue);
|
EXPORT_SYMBOL_GPL(bsg_unregister_queue);
|
||||||
|
|
||||||
int bsg_register_queue(struct request_queue *q, struct device *gdev,
|
int bsg_register_queue(struct request_queue *q, struct device *parent,
|
||||||
const char *name)
|
const char *name, void (*release)(struct device *))
|
||||||
{
|
{
|
||||||
struct bsg_class_device *bcd;
|
struct bsg_class_device *bcd;
|
||||||
dev_t dev;
|
dev_t dev;
|
||||||
|
@ -965,7 +978,7 @@ int bsg_register_queue(struct request_queue *q, struct device *gdev,
|
||||||
if (name)
|
if (name)
|
||||||
devname = name;
|
devname = name;
|
||||||
else
|
else
|
||||||
devname = gdev->bus_id;
|
devname = parent->bus_id;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* we need a proper transport to send commands, not a stacked device
|
* 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->minor = minor;
|
||||||
bcd->queue = q;
|
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);
|
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)) {
|
if (IS_ERR(class_dev)) {
|
||||||
ret = PTR_ERR(class_dev);
|
ret = PTR_ERR(class_dev);
|
||||||
goto put_dev;
|
goto put_dev;
|
||||||
|
@ -1017,7 +1032,7 @@ int bsg_register_queue(struct request_queue *q, struct device *gdev,
|
||||||
unregister_class_dev:
|
unregister_class_dev:
|
||||||
device_unregister(class_dev);
|
device_unregister(class_dev);
|
||||||
put_dev:
|
put_dev:
|
||||||
put_device(gdev);
|
put_device(parent);
|
||||||
remove_idr:
|
remove_idr:
|
||||||
idr_remove(&bsg_minor_idr, minor);
|
idr_remove(&bsg_minor_idr, minor);
|
||||||
unlock:
|
unlock:
|
||||||
|
|
|
@ -889,7 +889,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL);
|
error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL, NULL);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
sdev_printk(KERN_INFO, sdev,
|
sdev_printk(KERN_INFO, sdev,
|
||||||
|
|
|
@ -219,7 +219,7 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy)
|
||||||
if (!q)
|
if (!q)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
error = bsg_register_queue(q, dev, name);
|
error = bsg_register_queue(q, dev, name, NULL);
|
||||||
if (error) {
|
if (error) {
|
||||||
blk_cleanup_queue(q);
|
blk_cleanup_queue(q);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
|
@ -56,19 +56,25 @@ struct sg_io_v4 {
|
||||||
#if defined(CONFIG_BLK_DEV_BSG)
|
#if defined(CONFIG_BLK_DEV_BSG)
|
||||||
struct bsg_class_device {
|
struct bsg_class_device {
|
||||||
struct device *class_dev;
|
struct device *class_dev;
|
||||||
struct device *dev;
|
struct device *parent;
|
||||||
int minor;
|
int minor;
|
||||||
struct request_queue *queue;
|
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 *);
|
extern void bsg_unregister_queue(struct request_queue *);
|
||||||
#else
|
#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;
|
return 0;
|
||||||
}
|
}
|
||||||
static inline void bsg_unregister_queue(struct request_queue *rq)
|
static inline void bsg_unregister_queue(struct request_queue *q)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue