Commit b0b4e09c authored by Matias Bjørling's avatar Matias Bjørling Committed by Jens Axboe

lightnvm: control life of nvm_dev in driver

LightNVM compatible device drivers does not have a method to expose
LightNVM specific sysfs entries.

To enable LightNVM sysfs entries to be exposed, lightnvm device
drivers require a struct device to attach it to. To allow both the
actual device driver and lightnvm sysfs entries to coexist, the device
driver tracks the lifetime of the nvm_dev structure.

This patch refactors NVMe and null_blk to handle the lifetime of struct
nvm_dev, which eliminates the need for struct gendisk when a lightnvm
compatible device is provided.
Signed-off-by: default avatarMatias Bjørling <m@bjorling.me>
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
parent b21d5b30
...@@ -34,6 +34,7 @@ struct nullb { ...@@ -34,6 +34,7 @@ struct nullb {
unsigned int index; unsigned int index;
struct request_queue *q; struct request_queue *q;
struct gendisk *disk; struct gendisk *disk;
struct nvm_dev *ndev;
struct blk_mq_tag_set tag_set; struct blk_mq_tag_set tag_set;
struct hrtimer timer; struct hrtimer timer;
unsigned int queue_depth; unsigned int queue_depth;
...@@ -550,12 +551,29 @@ static struct nvm_dev_ops null_lnvm_dev_ops = { ...@@ -550,12 +551,29 @@ static struct nvm_dev_ops null_lnvm_dev_ops = {
static int null_nvm_register(struct nullb *nullb) static int null_nvm_register(struct nullb *nullb)
{ {
return nvm_register(nullb->q, nullb->disk_name, &null_lnvm_dev_ops); struct nvm_dev *dev;
int rv;
dev = nvm_alloc_dev(0);
if (!dev)
return -ENOMEM;
dev->q = nullb->q;
memcpy(dev->name, nullb->disk_name, DISK_NAME_LEN);
dev->ops = &null_lnvm_dev_ops;
rv = nvm_register(dev);
if (rv) {
kfree(dev);
return rv;
}
nullb->ndev = dev;
return 0;
} }
static void null_nvm_unregister(struct nullb *nullb) static void null_nvm_unregister(struct nullb *nullb)
{ {
nvm_unregister(nullb->disk_name); nvm_unregister(nullb->ndev);
} }
#else #else
static int null_nvm_register(struct nullb *nullb) static int null_nvm_register(struct nullb *nullb)
......
...@@ -660,22 +660,15 @@ static void nvm_exit(struct nvm_dev *dev) ...@@ -660,22 +660,15 @@ static void nvm_exit(struct nvm_dev *dev)
pr_info("nvm: successfully unloaded\n"); pr_info("nvm: successfully unloaded\n");
} }
int nvm_register(struct request_queue *q, char *disk_name, struct nvm_dev *nvm_alloc_dev(int node)
struct nvm_dev_ops *ops)
{ {
struct nvm_dev *dev; return kzalloc_node(sizeof(struct nvm_dev), GFP_KERNEL, node);
int ret; }
EXPORT_SYMBOL(nvm_alloc_dev);
if (!ops->identity)
return -EINVAL;
dev = kzalloc(sizeof(struct nvm_dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->q = q; int nvm_register(struct nvm_dev *dev)
dev->ops = ops; {
strncpy(dev->name, disk_name, DISK_NAME_LEN); int ret;
ret = nvm_init(dev); ret = nvm_init(dev);
if (ret) if (ret)
...@@ -714,29 +707,17 @@ int nvm_register(struct request_queue *q, char *disk_name, ...@@ -714,29 +707,17 @@ int nvm_register(struct request_queue *q, char *disk_name,
return 0; return 0;
err_init: err_init:
kfree(dev->lun_map); kfree(dev->lun_map);
kfree(dev);
return ret; return ret;
} }
EXPORT_SYMBOL(nvm_register); EXPORT_SYMBOL(nvm_register);
void nvm_unregister(char *disk_name) void nvm_unregister(struct nvm_dev *dev)
{ {
struct nvm_dev *dev;
down_write(&nvm_lock); down_write(&nvm_lock);
dev = nvm_find_nvm_dev(disk_name);
if (!dev) {
pr_err("nvm: could not find device %s to unregister\n",
disk_name);
up_write(&nvm_lock);
return;
}
list_del(&dev->devices); list_del(&dev->devices);
up_write(&nvm_lock); up_write(&nvm_lock);
nvm_exit(dev); nvm_exit(dev);
kfree(dev);
} }
EXPORT_SYMBOL(nvm_unregister); EXPORT_SYMBOL(nvm_unregister);
......
...@@ -156,12 +156,14 @@ static void nvme_free_ns(struct kref *kref) ...@@ -156,12 +156,14 @@ static void nvme_free_ns(struct kref *kref)
{ {
struct nvme_ns *ns = container_of(kref, struct nvme_ns, kref); struct nvme_ns *ns = container_of(kref, struct nvme_ns, kref);
if (ns->type == NVME_NS_LIGHTNVM) if (ns->ndev)
nvme_nvm_unregister(ns->queue, ns->disk->disk_name); nvme_nvm_unregister(ns);
if (ns->disk) {
spin_lock(&dev_list_lock); spin_lock(&dev_list_lock);
ns->disk->private_data = NULL; ns->disk->private_data = NULL;
spin_unlock(&dev_list_lock); spin_unlock(&dev_list_lock);
}
put_disk(ns->disk); put_disk(ns->disk);
ida_simple_remove(&ns->ctrl->ns_ida, ns->instance); ida_simple_remove(&ns->ctrl->ns_ida, ns->instance);
...@@ -891,8 +893,7 @@ static void nvme_config_discard(struct nvme_ns *ns) ...@@ -891,8 +893,7 @@ static void nvme_config_discard(struct nvme_ns *ns)
static int nvme_revalidate_ns(struct nvme_ns *ns, struct nvme_id_ns **id) static int nvme_revalidate_ns(struct nvme_ns *ns, struct nvme_id_ns **id)
{ {
if (nvme_identify_ns(ns->ctrl, ns->ns_id, id)) { if (nvme_identify_ns(ns->ctrl, ns->ns_id, id)) {
dev_warn(disk_to_dev(ns->disk), "%s: Identify failure\n", dev_warn(ns->ctrl->dev, "%s: Identify failure\n", __func__);
__func__);
return -ENODEV; return -ENODEV;
} }
...@@ -1683,18 +1684,11 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid) ...@@ -1683,18 +1684,11 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
goto out_free_queue; goto out_free_queue;
if (nvme_nvm_ns_supported(ns, id)) { if (nvme_nvm_ns_supported(ns, id)) {
if (nvme_nvm_register(ns->queue, disk_name)) { if (nvme_nvm_register(ns, disk_name, node)) {
dev_warn(ctrl->dev, dev_warn(ctrl->dev, "%s: LightNVM init failure\n",
"%s: LightNVM init failure\n", __func__); __func__);
goto out_free_id; goto out_free_id;
} }
disk = alloc_disk_node(0, node);
if (!disk)
goto out_free_id;
memcpy(disk->disk_name, disk_name, DISK_NAME_LEN);
ns->disk = disk;
ns->type = NVME_NS_LIGHTNVM;
} else { } else {
disk = alloc_disk_node(0, node); disk = alloc_disk_node(0, node);
if (!disk) if (!disk)
...@@ -1718,7 +1712,7 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid) ...@@ -1718,7 +1712,7 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
kfree(id); kfree(id);
if (ns->type == NVME_NS_LIGHTNVM) if (ns->ndev)
return; return;
device_add_disk(ctrl->device, ns->disk); device_add_disk(ctrl->device, ns->disk);
...@@ -1742,7 +1736,7 @@ static void nvme_ns_remove(struct nvme_ns *ns) ...@@ -1742,7 +1736,7 @@ static void nvme_ns_remove(struct nvme_ns *ns)
if (test_and_set_bit(NVME_NS_REMOVING, &ns->flags)) if (test_and_set_bit(NVME_NS_REMOVING, &ns->flags))
return; return;
if (ns->disk->flags & GENHD_FL_UP) { if (ns->disk && ns->disk->flags & GENHD_FL_UP) {
if (blk_get_integrity(ns->disk)) if (blk_get_integrity(ns->disk))
blk_integrity_unregister(ns->disk); blk_integrity_unregister(ns->disk);
sysfs_remove_group(&disk_to_dev(ns->disk)->kobj, sysfs_remove_group(&disk_to_dev(ns->disk)->kobj,
...@@ -1765,7 +1759,7 @@ static void nvme_validate_ns(struct nvme_ctrl *ctrl, unsigned nsid) ...@@ -1765,7 +1759,7 @@ static void nvme_validate_ns(struct nvme_ctrl *ctrl, unsigned nsid)
ns = nvme_find_get_ns(ctrl, nsid); ns = nvme_find_get_ns(ctrl, nsid);
if (ns) { if (ns) {
if (revalidate_disk(ns->disk)) if (ns->disk && revalidate_disk(ns->disk))
nvme_ns_remove(ns); nvme_ns_remove(ns);
nvme_put_ns(ns); nvme_put_ns(ns);
} else } else
...@@ -2070,7 +2064,7 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl) ...@@ -2070,7 +2064,7 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl)
* Revalidating a dead namespace sets capacity to 0. This will * Revalidating a dead namespace sets capacity to 0. This will
* end buffered writers dirtying pages that can't be synced. * end buffered writers dirtying pages that can't be synced.
*/ */
if (!test_and_set_bit(NVME_NS_DEAD, &ns->flags)) if (ns->disk && !test_and_set_bit(NVME_NS_DEAD, &ns->flags))
revalidate_disk(ns->disk); revalidate_disk(ns->disk);
blk_set_queue_dying(ns->queue); blk_set_queue_dying(ns->queue);
......
...@@ -474,9 +474,8 @@ static inline void nvme_nvm_rqtocmd(struct request *rq, struct nvm_rq *rqd, ...@@ -474,9 +474,8 @@ static inline void nvme_nvm_rqtocmd(struct request *rq, struct nvm_rq *rqd,
c->ph_rw.length = cpu_to_le16(rqd->nr_ppas - 1); c->ph_rw.length = cpu_to_le16(rqd->nr_ppas - 1);
if (rqd->opcode == NVM_OP_HBWRITE || rqd->opcode == NVM_OP_HBREAD) if (rqd->opcode == NVM_OP_HBWRITE || rqd->opcode == NVM_OP_HBREAD)
/* momentarily hardcode the shift configuration. lba_shift from c->hb_rw.slba = cpu_to_le64(nvme_block_nr(ns,
* nvm_dev will be available in a follow-up patch */ rqd->bio->bi_iter.bi_sector));
c->hb_rw.slba = cpu_to_le64(rqd->bio->bi_iter.bi_sector >> 3);
} }
static void nvme_nvm_end_io(struct request *rq, int error) static void nvme_nvm_end_io(struct request *rq, int error)
...@@ -593,14 +592,32 @@ static struct nvm_dev_ops nvme_nvm_dev_ops = { ...@@ -593,14 +592,32 @@ static struct nvm_dev_ops nvme_nvm_dev_ops = {
.max_phys_sect = 64, .max_phys_sect = 64,
}; };
int nvme_nvm_register(struct request_queue *q, char *disk_name) int nvme_nvm_register(struct nvme_ns *ns, char *disk_name, int node)
{ {
return nvm_register(q, disk_name, &nvme_nvm_dev_ops); struct request_queue *q = ns->queue;
struct nvm_dev *dev;
int ret;
dev = nvm_alloc_dev(node);
if (!dev)
return -ENOMEM;
dev->q = q;
memcpy(dev->name, disk_name, DISK_NAME_LEN);
dev->ops = &nvme_nvm_dev_ops;
ns->ndev = dev;
ret = nvm_register(dev);
ns->lba_shift = ilog2(dev->sec_size) - 9;
return ret;
} }
void nvme_nvm_unregister(struct request_queue *q, char *disk_name) void nvme_nvm_unregister(struct nvme_ns *ns)
{ {
nvm_unregister(disk_name); nvm_unregister(ns->ndev);
kfree(ns->ndev);
} }
/* move to shared place when used in multiple places. */ /* move to shared place when used in multiple places. */
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/blk-mq.h> #include <linux/blk-mq.h>
#include <linux/lightnvm.h>
enum { enum {
/* /*
...@@ -154,6 +155,7 @@ struct nvme_ns { ...@@ -154,6 +155,7 @@ struct nvme_ns {
struct nvme_ctrl *ctrl; struct nvme_ctrl *ctrl;
struct request_queue *queue; struct request_queue *queue;
struct gendisk *disk; struct gendisk *disk;
struct nvm_dev *ndev;
struct kref kref; struct kref kref;
int instance; int instance;
...@@ -165,7 +167,6 @@ struct nvme_ns { ...@@ -165,7 +167,6 @@ struct nvme_ns {
u16 ms; u16 ms;
bool ext; bool ext;
u8 pi_type; u8 pi_type;
int type;
unsigned long flags; unsigned long flags;
#define NVME_NS_REMOVING 0 #define NVME_NS_REMOVING 0
...@@ -307,15 +308,16 @@ int nvme_sg_get_version_num(int __user *ip); ...@@ -307,15 +308,16 @@ int nvme_sg_get_version_num(int __user *ip);
#ifdef CONFIG_NVM #ifdef CONFIG_NVM
int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id); 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); int nvme_nvm_register(struct nvme_ns *ns, char *disk_name, int node);
void nvme_nvm_unregister(struct request_queue *q, char *disk_name); void nvme_nvm_unregister(struct nvme_ns *ns);
#else #else
static inline int nvme_nvm_register(struct request_queue *q, char *disk_name) static inline int nvme_nvm_register(struct nvme_ns *ns, char *disk_name,
int node)
{ {
return 0; return 0;
} }
static inline void nvme_nvm_unregister(struct request_queue *q, char *disk_name) {}; static inline void nvme_nvm_unregister(struct nvme_ns *ns) {};
static inline int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id) static inline int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id)
{ {
......
...@@ -524,9 +524,9 @@ extern struct nvm_block *nvm_get_blk(struct nvm_dev *, struct nvm_lun *, ...@@ -524,9 +524,9 @@ extern struct nvm_block *nvm_get_blk(struct nvm_dev *, struct nvm_lun *,
unsigned long); unsigned long);
extern void nvm_put_blk(struct nvm_dev *, struct nvm_block *); extern void nvm_put_blk(struct nvm_dev *, struct nvm_block *);
extern int nvm_register(struct request_queue *, char *, extern struct nvm_dev *nvm_alloc_dev(int);
struct nvm_dev_ops *); extern int nvm_register(struct nvm_dev *);
extern void nvm_unregister(char *); extern void nvm_unregister(struct nvm_dev *);
void nvm_mark_blk(struct nvm_dev *dev, struct ppa_addr ppa, int type); void nvm_mark_blk(struct nvm_dev *dev, struct ppa_addr ppa, int type);
...@@ -575,11 +575,14 @@ extern int nvm_dev_factory(struct nvm_dev *, int flags); ...@@ -575,11 +575,14 @@ extern int nvm_dev_factory(struct nvm_dev *, int flags);
#else /* CONFIG_NVM */ #else /* CONFIG_NVM */
struct nvm_dev_ops; struct nvm_dev_ops;
static inline int nvm_register(struct request_queue *q, char *disk_name, static inline struct nvm_dev *nvm_alloc_dev(int node)
struct nvm_dev_ops *ops) {
return ERR_PTR(-EINVAL);
}
static inline int nvm_register(struct nvm_dev *dev)
{ {
return -EINVAL; return -EINVAL;
} }
static inline void nvm_unregister(char *disk_name) {} static inline void nvm_unregister(struct nvm_dev *dev) {}
#endif /* CONFIG_NVM */ #endif /* CONFIG_NVM */
#endif /* LIGHTNVM.H */ #endif /* LIGHTNVM.H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment