Skip to content

Commit

Permalink
nvme: move namespace scanning to common code
Browse files Browse the repository at this point in the history
The namespace scanning code has been mostly generic already, we just
need to store a pointer to the tagset in the nvme_ctrl structure, and
add a method to check if a controller is I/O incapable.  The latter
will hopefully be replaced by a proper controller state machine soon.

Signed-off-by: Christoph Hellwig <hch@lst.de>
[Fixed pr conflicts]
Signed-off-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
  • Loading branch information
Christoph Hellwig authored and Jens Axboe committed Dec 1, 2015
1 parent ce4541f commit 5bae7f7
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 196 deletions.
192 changes: 189 additions & 3 deletions drivers/nvme/host/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <linux/errno.h>
#include <linux/hdreg.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/list_sort.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/pr.h>
Expand All @@ -29,6 +31,9 @@

#include "nvme.h"

static int nvme_major;
module_param(nvme_major, int, 0);

DEFINE_SPINLOCK(dev_list_lock);

static void nvme_free_ns(struct kref *kref)
Expand All @@ -47,7 +52,7 @@ static void nvme_free_ns(struct kref *kref)
kfree(ns);
}

void nvme_put_ns(struct nvme_ns *ns)
static void nvme_put_ns(struct nvme_ns *ns)
{
kref_put(&ns->kref, nvme_free_ns);
}
Expand Down Expand Up @@ -496,7 +501,7 @@ static void nvme_config_discard(struct nvme_ns *ns)
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, ns->queue);
}

int nvme_revalidate_disk(struct gendisk *disk)
static int nvme_revalidate_disk(struct gendisk *disk)
{
struct nvme_ns *ns = disk->private_data;
struct nvme_id_ns *id;
Expand Down Expand Up @@ -660,7 +665,7 @@ static const struct pr_ops nvme_pr_ops = {
.pr_clear = nvme_pr_clear,
};

const struct block_device_operations nvme_fops = {
static const struct block_device_operations nvme_fops = {
.owner = THIS_MODULE,
.ioctl = nvme_ioctl,
.compat_ioctl = nvme_compat_ioctl,
Expand Down Expand Up @@ -840,3 +845,184 @@ void nvme_put_ctrl(struct nvme_ctrl *ctrl)
kref_put(&ctrl->kref, nvme_free_ctrl);
}

static int ns_cmp(void *priv, struct list_head *a, struct list_head *b)
{
struct nvme_ns *nsa = container_of(a, struct nvme_ns, list);
struct nvme_ns *nsb = container_of(b, struct nvme_ns, list);

return nsa->ns_id - nsb->ns_id;
}

static struct nvme_ns *nvme_find_ns(struct nvme_ctrl *ctrl, unsigned nsid)
{
struct nvme_ns *ns;

list_for_each_entry(ns, &ctrl->namespaces, list) {
if (ns->ns_id == nsid)
return ns;
if (ns->ns_id > nsid)
break;
}
return NULL;
}

static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
{
struct nvme_ns *ns;
struct gendisk *disk;
int node = dev_to_node(ctrl->dev);

ns = kzalloc_node(sizeof(*ns), GFP_KERNEL, node);
if (!ns)
return;

ns->queue = blk_mq_init_queue(ctrl->tagset);
if (IS_ERR(ns->queue))
goto out_free_ns;
queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, ns->queue);
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, ns->queue);
ns->queue->queuedata = ns;
ns->ctrl = ctrl;

disk = alloc_disk_node(0, node);
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 */
list_add_tail(&ns->list, &ctrl->namespaces);

blk_queue_logical_block_size(ns->queue, 1 << ns->lba_shift);
if (ctrl->max_hw_sectors) {
blk_queue_max_hw_sectors(ns->queue, ctrl->max_hw_sectors);
blk_queue_max_segments(ns->queue,
(ctrl->max_hw_sectors / (ctrl->page_size >> 9)) + 1);
}
if (ctrl->stripe_size)
blk_queue_chunk_sectors(ns->queue, ctrl->stripe_size >> 9);
if (ctrl->vwc & NVME_CTRL_VWC_PRESENT)
blk_queue_flush(ns->queue, REQ_FLUSH | REQ_FUA);
blk_queue_virt_boundary(ns->queue, ctrl->page_size - 1);

disk->major = nvme_major;
disk->first_minor = 0;
disk->fops = &nvme_fops;
disk->private_data = ns;
disk->queue = ns->queue;
disk->driverfs_dev = ctrl->device;
disk->flags = GENHD_FL_EXT_DEVT;
sprintf(disk->disk_name, "nvme%dn%d", ctrl->instance, nsid);

/*
* Initialize capacity to 0 until we establish the namespace format and
* setup integrity extentions if necessary. The revalidate_disk after
* add_disk allows the driver to register with integrity if the format
* requires it.
*/
set_capacity(disk, 0);
if (nvme_revalidate_disk(ns->disk))
goto out_free_disk;

kref_get(&ctrl->kref);
if (ns->type != NVME_NS_LIGHTNVM) {
add_disk(ns->disk);
if (ns->ms) {
struct block_device *bd = bdget_disk(ns->disk, 0);
if (!bd)
return;
if (blkdev_get(bd, FMODE_READ, NULL)) {
bdput(bd);
return;
}
blkdev_reread_part(bd);
blkdev_put(bd, FMODE_READ);
}
}

return;
out_free_disk:
kfree(disk);
list_del(&ns->list);
out_free_queue:
blk_cleanup_queue(ns->queue);
out_free_ns:
kfree(ns);
}

static void nvme_ns_remove(struct nvme_ns *ns)
{
bool kill = nvme_io_incapable(ns->ctrl) &&
!blk_queue_dying(ns->queue);

if (kill)
blk_set_queue_dying(ns->queue);
if (ns->disk->flags & GENHD_FL_UP) {
if (blk_get_integrity(ns->disk))
blk_integrity_unregister(ns->disk);
del_gendisk(ns->disk);
}
if (kill || !blk_queue_dying(ns->queue)) {
blk_mq_abort_requeue_list(ns->queue);
blk_cleanup_queue(ns->queue);
}
list_del_init(&ns->list);
nvme_put_ns(ns);
}

static void __nvme_scan_namespaces(struct nvme_ctrl *ctrl, unsigned nn)
{
struct nvme_ns *ns, *next;
unsigned i;

for (i = 1; i <= nn; i++) {
ns = nvme_find_ns(ctrl, i);
if (ns) {
if (revalidate_disk(ns->disk))
nvme_ns_remove(ns);
} else
nvme_alloc_ns(ctrl, i);
}
list_for_each_entry_safe(ns, next, &ctrl->namespaces, list) {
if (ns->ns_id > nn)
nvme_ns_remove(ns);
}
list_sort(NULL, &ctrl->namespaces, ns_cmp);
}

void nvme_scan_namespaces(struct nvme_ctrl *ctrl)
{
struct nvme_id_ctrl *id;

if (nvme_identify_ctrl(ctrl, &id))
return;
__nvme_scan_namespaces(ctrl, le32_to_cpup(&id->nn));
kfree(id);
}

void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
{
struct nvme_ns *ns, *next;

list_for_each_entry_safe(ns, next, &ctrl->namespaces, list)
nvme_ns_remove(ns);
}

int __init nvme_core_init(void)
{
int result;

result = register_blkdev(nvme_major, "nvme");
if (result < 0)
return result;
else if (result > 0)
nvme_major = result;

return 0;
}

void nvme_core_exit(void)
{
unregister_blkdev(nvme_major, "nvme");
}
24 changes: 21 additions & 3 deletions drivers/nvme/host/nvme.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ struct nvme_ctrl {
struct device *dev;
struct kref kref;
int instance;
struct blk_mq_tag_set *tagset;
struct list_head namespaces;
struct device *device; /* char device */

char name[12];
char serial[20];
Expand Down Expand Up @@ -96,6 +99,7 @@ struct nvme_ctrl_ops {
int (*reg_read32)(struct nvme_ctrl *ctrl, u32 off, u32 *val);
int (*reg_write32)(struct nvme_ctrl *ctrl, u32 off, u32 val);
int (*reg_read64)(struct nvme_ctrl *ctrl, u32 off, u64 *val);
bool (*io_incapable)(struct nvme_ctrl *ctrl);
void (*free_ctrl)(struct nvme_ctrl *ctrl);
};

Expand All @@ -108,6 +112,17 @@ static inline bool nvme_ctrl_ready(struct nvme_ctrl *ctrl)
return val & NVME_CSTS_RDY;
}

static inline bool nvme_io_incapable(struct nvme_ctrl *ctrl)
{
u32 val = 0;

if (ctrl->ops->io_incapable(ctrl))
return false;
if (ctrl->ops->reg_read32(ctrl, NVME_REG_CSTS, &val))
return false;
return val & NVME_CSTS_CFS;
}

static inline u64 nvme_block_nr(struct nvme_ns *ns, sector_t sector)
{
return (sector >> (ns->lba_shift - 9));
Expand Down Expand Up @@ -181,7 +196,9 @@ int nvme_enable_ctrl(struct nvme_ctrl *ctrl, u64 cap);
int nvme_shutdown_ctrl(struct nvme_ctrl *ctrl);
void nvme_put_ctrl(struct nvme_ctrl *ctrl);
int nvme_init_identify(struct nvme_ctrl *ctrl);
void nvme_put_ns(struct nvme_ns *ns);

void nvme_scan_namespaces(struct nvme_ctrl *ctrl);
void nvme_remove_namespaces(struct nvme_ctrl *ctrl);

struct request *nvme_alloc_request(struct request_queue *q,
struct nvme_command *cmd, unsigned int flags);
Expand All @@ -205,10 +222,8 @@ int nvme_get_features(struct nvme_ctrl *dev, unsigned fid, unsigned nsid,
int nvme_set_features(struct nvme_ctrl *dev, unsigned fid, unsigned dword11,
dma_addr_t dma_addr, u32 *result);

extern const struct block_device_operations nvme_fops;
extern spinlock_t dev_list_lock;

int nvme_revalidate_disk(struct gendisk *disk);
int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
struct nvme_passthru_cmd __user *ucmd);

Expand All @@ -222,4 +237,7 @@ int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id);
int nvme_nvm_register(struct request_queue *q, char *disk_name);
void nvme_nvm_unregister(struct request_queue *q, char *disk_name);

int __init nvme_core_init(void);
void nvme_core_exit(void);

#endif /* _NVME_H */
Loading

0 comments on commit 5bae7f7

Please sign in to comment.