Skip to content

Commit

Permalink
NVMe: Reference count open namespaces
Browse files Browse the repository at this point in the history
Dynamic namespace attachment means the namespace may be removed at any
time, so the namespace reference count can not be tied to the device
reference count. This fixes a NULL dereference if an opened namespace
is detached from a controller.

Signed-off-by: Keith Busch <keith.busch@intel.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@fb.com>
  • Loading branch information
Keith Busch authored and Jens Axboe committed Oct 9, 2015
1 parent 54ef2b9 commit 188c356
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 9 deletions.
29 changes: 20 additions & 9 deletions drivers/block/nvme-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1943,6 +1943,18 @@ static int nvme_compat_ioctl(struct block_device *bdev, fmode_t mode,
#define nvme_compat_ioctl NULL
#endif

static void nvme_free_ns(struct kref *kref)
{
struct nvme_ns *ns = container_of(kref, struct nvme_ns, kref);

spin_lock(&dev_list_lock);
ns->disk->private_data = NULL;
spin_unlock(&dev_list_lock);

put_disk(ns->disk);
kfree(ns);
}

static int nvme_open(struct block_device *bdev, fmode_t mode)
{
int ret = 0;
Expand All @@ -1952,21 +1964,25 @@ static int nvme_open(struct block_device *bdev, fmode_t mode)
ns = bdev->bd_disk->private_data;
if (!ns)
ret = -ENXIO;
else if (!kref_get_unless_zero(&ns->dev->kref))
else if (!kref_get_unless_zero(&ns->kref))
ret = -ENXIO;
else if (!kref_get_unless_zero(&ns->dev->kref)) {
kref_put(&ns->kref, nvme_free_ns);
ret = -ENXIO;
}
spin_unlock(&dev_list_lock);

return ret;
}

static void nvme_free_dev(struct kref *kref);

static void nvme_release(struct gendisk *disk, fmode_t mode)
{
struct nvme_ns *ns = disk->private_data;
struct nvme_dev *dev = ns->dev;

kref_put(&dev->kref, nvme_free_dev);
kref_put(&ns->kref, nvme_free_ns);
}

static int nvme_getgeo(struct block_device *bd, struct hd_geometry *geo)
Expand Down Expand Up @@ -2126,6 +2142,7 @@ static void nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid)
if (!disk)
goto out_free_queue;

kref_init(&ns->kref);
ns->ns_id = nsid;
ns->disk = disk;
ns->lba_shift = 9; /* set to a default value for 512 until disk is validated */
Expand Down Expand Up @@ -2360,13 +2377,7 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
static void nvme_free_namespace(struct nvme_ns *ns)
{
list_del(&ns->list);

spin_lock(&dev_list_lock);
ns->disk->private_data = NULL;
spin_unlock(&dev_list_lock);

put_disk(ns->disk);
kfree(ns);
kref_put(&ns->kref, nvme_free_ns);
}

static int ns_cmp(void *priv, struct list_head *a, struct list_head *b)
Expand Down
1 change: 1 addition & 0 deletions include/linux/nvme.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ struct nvme_ns {
struct nvme_dev *dev;
struct request_queue *queue;
struct gendisk *disk;
struct kref kref;

unsigned ns_id;
int lba_shift;
Expand Down

0 comments on commit 188c356

Please sign in to comment.