diff options
author | Matias Bjørling <m@bjorling.me> | 2016-09-16 08:25:07 -0400 |
---|---|---|
committer | Jens Axboe <axboe@fb.com> | 2016-09-21 09:56:18 -0400 |
commit | b0b4e09c1ae71c4ec33df0616b830ae050006e9b (patch) | |
tree | 67a148740845d77af662b36ec17d88f991c84d72 | |
parent | b21d5b301794ae332eaa6e177d71fe8b77d3664c (diff) |
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: Matias Bjørling <m@bjorling.me>
Signed-off-by: Jens Axboe <axboe@fb.com>
-rw-r--r-- | drivers/block/null_blk.c | 22 | ||||
-rw-r--r-- | drivers/lightnvm/core.c | 35 | ||||
-rw-r--r-- | drivers/nvme/host/core.c | 36 | ||||
-rw-r--r-- | drivers/nvme/host/lightnvm.c | 31 | ||||
-rw-r--r-- | drivers/nvme/host/nvme.h | 12 | ||||
-rw-r--r-- | include/linux/lightnvm.h | 15 |
6 files changed, 83 insertions, 68 deletions
diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c index 895867a8a783..91e1de898daf 100644 --- a/drivers/block/null_blk.c +++ b/drivers/block/null_blk.c | |||
@@ -34,6 +34,7 @@ struct nullb { | |||
34 | unsigned int index; | 34 | unsigned int index; |
35 | struct request_queue *q; | 35 | struct request_queue *q; |
36 | struct gendisk *disk; | 36 | struct gendisk *disk; |
37 | struct nvm_dev *ndev; | ||
37 | struct blk_mq_tag_set tag_set; | 38 | struct blk_mq_tag_set tag_set; |
38 | struct hrtimer timer; | 39 | struct hrtimer timer; |
39 | unsigned int queue_depth; | 40 | unsigned int queue_depth; |
@@ -550,12 +551,29 @@ static struct nvm_dev_ops null_lnvm_dev_ops = { | |||
550 | 551 | ||
551 | static int null_nvm_register(struct nullb *nullb) | 552 | static int null_nvm_register(struct nullb *nullb) |
552 | { | 553 | { |
553 | return nvm_register(nullb->q, nullb->disk_name, &null_lnvm_dev_ops); | 554 | struct nvm_dev *dev; |
555 | int rv; | ||
556 | |||
557 | dev = nvm_alloc_dev(0); | ||
558 | if (!dev) | ||
559 | return -ENOMEM; | ||
560 | |||
561 | dev->q = nullb->q; | ||
562 | memcpy(dev->name, nullb->disk_name, DISK_NAME_LEN); | ||
563 | dev->ops = &null_lnvm_dev_ops; | ||
564 | |||
565 | rv = nvm_register(dev); | ||
566 | if (rv) { | ||
567 | kfree(dev); | ||
568 | return rv; | ||
569 | } | ||
570 | nullb->ndev = dev; | ||
571 | return 0; | ||
554 | } | 572 | } |
555 | 573 | ||
556 | static void null_nvm_unregister(struct nullb *nullb) | 574 | static void null_nvm_unregister(struct nullb *nullb) |
557 | { | 575 | { |
558 | nvm_unregister(nullb->disk_name); | 576 | nvm_unregister(nullb->ndev); |
559 | } | 577 | } |
560 | #else | 578 | #else |
561 | static int null_nvm_register(struct nullb *nullb) | 579 | static int null_nvm_register(struct nullb *nullb) |
diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c index 25c5df920326..a99b59d1eb36 100644 --- a/drivers/lightnvm/core.c +++ b/drivers/lightnvm/core.c | |||
@@ -660,22 +660,15 @@ static void nvm_exit(struct nvm_dev *dev) | |||
660 | pr_info("nvm: successfully unloaded\n"); | 660 | pr_info("nvm: successfully unloaded\n"); |
661 | } | 661 | } |
662 | 662 | ||
663 | int nvm_register(struct request_queue *q, char *disk_name, | 663 | struct nvm_dev *nvm_alloc_dev(int node) |
664 | struct nvm_dev_ops *ops) | ||
665 | { | 664 | { |
666 | struct nvm_dev *dev; | 665 | return kzalloc_node(sizeof(struct nvm_dev), GFP_KERNEL, node); |
667 | int ret; | 666 | } |
668 | 667 | EXPORT_SYMBOL(nvm_alloc_dev); | |
669 | if (!ops->identity) | ||
670 | return -EINVAL; | ||
671 | |||
672 | dev = kzalloc(sizeof(struct nvm_dev), GFP_KERNEL); | ||
673 | if (!dev) | ||
674 | return -ENOMEM; | ||
675 | 668 | ||
676 | dev->q = q; | 669 | int nvm_register(struct nvm_dev *dev) |
677 | dev->ops = ops; | 670 | { |
678 | strncpy(dev->name, disk_name, DISK_NAME_LEN); | 671 | int ret; |
679 | 672 | ||
680 | ret = nvm_init(dev); | 673 | ret = nvm_init(dev); |
681 | if (ret) | 674 | if (ret) |
@@ -714,29 +707,17 @@ int nvm_register(struct request_queue *q, char *disk_name, | |||
714 | return 0; | 707 | return 0; |
715 | err_init: | 708 | err_init: |
716 | kfree(dev->lun_map); | 709 | kfree(dev->lun_map); |
717 | kfree(dev); | ||
718 | return ret; | 710 | return ret; |
719 | } | 711 | } |
720 | EXPORT_SYMBOL(nvm_register); | 712 | EXPORT_SYMBOL(nvm_register); |
721 | 713 | ||
722 | void nvm_unregister(char *disk_name) | 714 | void nvm_unregister(struct nvm_dev *dev) |
723 | { | 715 | { |
724 | struct nvm_dev *dev; | ||
725 | |||
726 | down_write(&nvm_lock); | 716 | down_write(&nvm_lock); |
727 | dev = nvm_find_nvm_dev(disk_name); | ||
728 | if (!dev) { | ||
729 | pr_err("nvm: could not find device %s to unregister\n", | ||
730 | disk_name); | ||
731 | up_write(&nvm_lock); | ||
732 | return; | ||
733 | } | ||
734 | |||
735 | list_del(&dev->devices); | 717 | list_del(&dev->devices); |
736 | up_write(&nvm_lock); | 718 | up_write(&nvm_lock); |
737 | 719 | ||
738 | nvm_exit(dev); | 720 | nvm_exit(dev); |
739 | kfree(dev); | ||
740 | } | 721 | } |
741 | EXPORT_SYMBOL(nvm_unregister); | 722 | EXPORT_SYMBOL(nvm_unregister); |
742 | 723 | ||
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 2c3da3315a02..3c707d83b1da 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c | |||
@@ -156,12 +156,14 @@ static void nvme_free_ns(struct kref *kref) | |||
156 | { | 156 | { |
157 | struct nvme_ns *ns = container_of(kref, struct nvme_ns, kref); | 157 | struct nvme_ns *ns = container_of(kref, struct nvme_ns, kref); |
158 | 158 | ||
159 | if (ns->type == NVME_NS_LIGHTNVM) | 159 | if (ns->ndev) |
160 | nvme_nvm_unregister(ns->queue, ns->disk->disk_name); | 160 | nvme_nvm_unregister(ns); |
161 | 161 | ||
162 | spin_lock(&dev_list_lock); | 162 | if (ns->disk) { |
163 | ns->disk->private_data = NULL; | 163 | spin_lock(&dev_list_lock); |
164 | spin_unlock(&dev_list_lock); | 164 | ns->disk->private_data = NULL; |
165 | spin_unlock(&dev_list_lock); | ||
166 | } | ||
165 | 167 | ||
166 | put_disk(ns->disk); | 168 | put_disk(ns->disk); |
167 | ida_simple_remove(&ns->ctrl->ns_ida, ns->instance); | 169 | ida_simple_remove(&ns->ctrl->ns_ida, ns->instance); |
@@ -891,8 +893,7 @@ static void nvme_config_discard(struct nvme_ns *ns) | |||
891 | static int nvme_revalidate_ns(struct nvme_ns *ns, struct nvme_id_ns **id) | 893 | static int nvme_revalidate_ns(struct nvme_ns *ns, struct nvme_id_ns **id) |
892 | { | 894 | { |
893 | if (nvme_identify_ns(ns->ctrl, ns->ns_id, id)) { | 895 | if (nvme_identify_ns(ns->ctrl, ns->ns_id, id)) { |
894 | dev_warn(disk_to_dev(ns->disk), "%s: Identify failure\n", | 896 | dev_warn(ns->ctrl->dev, "%s: Identify failure\n", __func__); |
895 | __func__); | ||
896 | return -ENODEV; | 897 | return -ENODEV; |
897 | } | 898 | } |
898 | 899 | ||
@@ -1683,18 +1684,11 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid) | |||
1683 | goto out_free_queue; | 1684 | goto out_free_queue; |
1684 | 1685 | ||
1685 | if (nvme_nvm_ns_supported(ns, id)) { | 1686 | if (nvme_nvm_ns_supported(ns, id)) { |
1686 | if (nvme_nvm_register(ns->queue, disk_name)) { | 1687 | if (nvme_nvm_register(ns, disk_name, node)) { |
1687 | dev_warn(ctrl->dev, | 1688 | dev_warn(ctrl->dev, "%s: LightNVM init failure\n", |
1688 | "%s: LightNVM init failure\n", __func__); | 1689 | __func__); |
1689 | goto out_free_id; | 1690 | goto out_free_id; |
1690 | } | 1691 | } |
1691 | |||
1692 | disk = alloc_disk_node(0, node); | ||
1693 | if (!disk) | ||
1694 | goto out_free_id; | ||
1695 | memcpy(disk->disk_name, disk_name, DISK_NAME_LEN); | ||
1696 | ns->disk = disk; | ||
1697 | ns->type = NVME_NS_LIGHTNVM; | ||
1698 | } else { | 1692 | } else { |
1699 | disk = alloc_disk_node(0, node); | 1693 | disk = alloc_disk_node(0, node); |
1700 | if (!disk) | 1694 | if (!disk) |
@@ -1718,7 +1712,7 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid) | |||
1718 | 1712 | ||
1719 | kfree(id); | 1713 | kfree(id); |
1720 | 1714 | ||
1721 | if (ns->type == NVME_NS_LIGHTNVM) | 1715 | if (ns->ndev) |
1722 | return; | 1716 | return; |
1723 | 1717 | ||
1724 | device_add_disk(ctrl->device, ns->disk); | 1718 | device_add_disk(ctrl->device, ns->disk); |
@@ -1742,7 +1736,7 @@ static void nvme_ns_remove(struct nvme_ns *ns) | |||
1742 | if (test_and_set_bit(NVME_NS_REMOVING, &ns->flags)) | 1736 | if (test_and_set_bit(NVME_NS_REMOVING, &ns->flags)) |
1743 | return; | 1737 | return; |
1744 | 1738 | ||
1745 | if (ns->disk->flags & GENHD_FL_UP) { | 1739 | if (ns->disk && ns->disk->flags & GENHD_FL_UP) { |
1746 | if (blk_get_integrity(ns->disk)) | 1740 | if (blk_get_integrity(ns->disk)) |
1747 | blk_integrity_unregister(ns->disk); | 1741 | blk_integrity_unregister(ns->disk); |
1748 | sysfs_remove_group(&disk_to_dev(ns->disk)->kobj, | 1742 | 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 | 1759 | ||
1766 | ns = nvme_find_get_ns(ctrl, nsid); | 1760 | ns = nvme_find_get_ns(ctrl, nsid); |
1767 | if (ns) { | 1761 | if (ns) { |
1768 | if (revalidate_disk(ns->disk)) | 1762 | if (ns->disk && revalidate_disk(ns->disk)) |
1769 | nvme_ns_remove(ns); | 1763 | nvme_ns_remove(ns); |
1770 | nvme_put_ns(ns); | 1764 | nvme_put_ns(ns); |
1771 | } else | 1765 | } else |
@@ -2070,7 +2064,7 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl) | |||
2070 | * Revalidating a dead namespace sets capacity to 0. This will | 2064 | * Revalidating a dead namespace sets capacity to 0. This will |
2071 | * end buffered writers dirtying pages that can't be synced. | 2065 | * end buffered writers dirtying pages that can't be synced. |
2072 | */ | 2066 | */ |
2073 | if (!test_and_set_bit(NVME_NS_DEAD, &ns->flags)) | 2067 | if (ns->disk && !test_and_set_bit(NVME_NS_DEAD, &ns->flags)) |
2074 | revalidate_disk(ns->disk); | 2068 | revalidate_disk(ns->disk); |
2075 | 2069 | ||
2076 | blk_set_queue_dying(ns->queue); | 2070 | blk_set_queue_dying(ns->queue); |
diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c index 7268a7a1a19a..798fcd9f5d1f 100644 --- a/drivers/nvme/host/lightnvm.c +++ b/drivers/nvme/host/lightnvm.c | |||
@@ -474,9 +474,8 @@ static inline void nvme_nvm_rqtocmd(struct request *rq, struct nvm_rq *rqd, | |||
474 | c->ph_rw.length = cpu_to_le16(rqd->nr_ppas - 1); | 474 | c->ph_rw.length = cpu_to_le16(rqd->nr_ppas - 1); |
475 | 475 | ||
476 | if (rqd->opcode == NVM_OP_HBWRITE || rqd->opcode == NVM_OP_HBREAD) | 476 | if (rqd->opcode == NVM_OP_HBWRITE || rqd->opcode == NVM_OP_HBREAD) |
477 | /* momentarily hardcode the shift configuration. lba_shift from | 477 | c->hb_rw.slba = cpu_to_le64(nvme_block_nr(ns, |
478 | * nvm_dev will be available in a follow-up patch */ | 478 | rqd->bio->bi_iter.bi_sector)); |
479 | c->hb_rw.slba = cpu_to_le64(rqd->bio->bi_iter.bi_sector >> 3); | ||
480 | } | 479 | } |
481 | 480 | ||
482 | static void nvme_nvm_end_io(struct request *rq, int error) | 481 | 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 | .max_phys_sect = 64, | 592 | .max_phys_sect = 64, |
594 | }; | 593 | }; |
595 | 594 | ||
596 | int nvme_nvm_register(struct request_queue *q, char *disk_name) | 595 | int nvme_nvm_register(struct nvme_ns *ns, char *disk_name, int node) |
597 | { | 596 | { |
598 | return nvm_register(q, disk_name, &nvme_nvm_dev_ops); | 597 | struct request_queue *q = ns->queue; |
598 | struct nvm_dev *dev; | ||
599 | int ret; | ||
600 | |||
601 | dev = nvm_alloc_dev(node); | ||
602 | if (!dev) | ||
603 | return -ENOMEM; | ||
604 | |||
605 | dev->q = q; | ||
606 | memcpy(dev->name, disk_name, DISK_NAME_LEN); | ||
607 | dev->ops = &nvme_nvm_dev_ops; | ||
608 | ns->ndev = dev; | ||
609 | |||
610 | ret = nvm_register(dev); | ||
611 | |||
612 | ns->lba_shift = ilog2(dev->sec_size) - 9; | ||
613 | |||
614 | return ret; | ||
599 | } | 615 | } |
600 | 616 | ||
601 | void nvme_nvm_unregister(struct request_queue *q, char *disk_name) | 617 | void nvme_nvm_unregister(struct nvme_ns *ns) |
602 | { | 618 | { |
603 | nvm_unregister(disk_name); | 619 | nvm_unregister(ns->ndev); |
620 | kfree(ns->ndev); | ||
604 | } | 621 | } |
605 | 622 | ||
606 | /* move to shared place when used in multiple places. */ | 623 | /* move to shared place when used in multiple places. */ |
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index ab18b78102bf..e0535c14e538 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/pci.h> | 18 | #include <linux/pci.h> |
19 | #include <linux/kref.h> | 19 | #include <linux/kref.h> |
20 | #include <linux/blk-mq.h> | 20 | #include <linux/blk-mq.h> |
21 | #include <linux/lightnvm.h> | ||
21 | 22 | ||
22 | enum { | 23 | enum { |
23 | /* | 24 | /* |
@@ -154,6 +155,7 @@ struct nvme_ns { | |||
154 | struct nvme_ctrl *ctrl; | 155 | struct nvme_ctrl *ctrl; |
155 | struct request_queue *queue; | 156 | struct request_queue *queue; |
156 | struct gendisk *disk; | 157 | struct gendisk *disk; |
158 | struct nvm_dev *ndev; | ||
157 | struct kref kref; | 159 | struct kref kref; |
158 | int instance; | 160 | int instance; |
159 | 161 | ||
@@ -165,7 +167,6 @@ struct nvme_ns { | |||
165 | u16 ms; | 167 | u16 ms; |
166 | bool ext; | 168 | bool ext; |
167 | u8 pi_type; | 169 | u8 pi_type; |
168 | int type; | ||
169 | unsigned long flags; | 170 | unsigned long flags; |
170 | 171 | ||
171 | #define NVME_NS_REMOVING 0 | 172 | #define NVME_NS_REMOVING 0 |
@@ -307,15 +308,16 @@ int nvme_sg_get_version_num(int __user *ip); | |||
307 | 308 | ||
308 | #ifdef CONFIG_NVM | 309 | #ifdef CONFIG_NVM |
309 | int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id); | 310 | int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id); |
310 | int nvme_nvm_register(struct request_queue *q, char *disk_name); | 311 | int nvme_nvm_register(struct nvme_ns *ns, char *disk_name, int node); |
311 | void nvme_nvm_unregister(struct request_queue *q, char *disk_name); | 312 | void nvme_nvm_unregister(struct nvme_ns *ns); |
312 | #else | 313 | #else |
313 | static inline int nvme_nvm_register(struct request_queue *q, char *disk_name) | 314 | static inline int nvme_nvm_register(struct nvme_ns *ns, char *disk_name, |
315 | int node) | ||
314 | { | 316 | { |
315 | return 0; | 317 | return 0; |
316 | } | 318 | } |
317 | 319 | ||
318 | static inline void nvme_nvm_unregister(struct request_queue *q, char *disk_name) {}; | 320 | static inline void nvme_nvm_unregister(struct nvme_ns *ns) {}; |
319 | 321 | ||
320 | static inline int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id) | 322 | static inline int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id) |
321 | { | 323 | { |
diff --git a/include/linux/lightnvm.h b/include/linux/lightnvm.h index ba78b8306674..5afc2634f332 100644 --- a/include/linux/lightnvm.h +++ b/include/linux/lightnvm.h | |||
@@ -524,9 +524,9 @@ extern struct nvm_block *nvm_get_blk(struct nvm_dev *, struct nvm_lun *, | |||
524 | unsigned long); | 524 | unsigned long); |
525 | extern void nvm_put_blk(struct nvm_dev *, struct nvm_block *); | 525 | extern void nvm_put_blk(struct nvm_dev *, struct nvm_block *); |
526 | 526 | ||
527 | extern int nvm_register(struct request_queue *, char *, | 527 | extern struct nvm_dev *nvm_alloc_dev(int); |
528 | struct nvm_dev_ops *); | 528 | extern int nvm_register(struct nvm_dev *); |
529 | extern void nvm_unregister(char *); | 529 | extern void nvm_unregister(struct nvm_dev *); |
530 | 530 | ||
531 | void nvm_mark_blk(struct nvm_dev *dev, struct ppa_addr ppa, int type); | 531 | void nvm_mark_blk(struct nvm_dev *dev, struct ppa_addr ppa, int type); |
532 | 532 | ||
@@ -575,11 +575,14 @@ extern int nvm_dev_factory(struct nvm_dev *, int flags); | |||
575 | #else /* CONFIG_NVM */ | 575 | #else /* CONFIG_NVM */ |
576 | struct nvm_dev_ops; | 576 | struct nvm_dev_ops; |
577 | 577 | ||
578 | static inline int nvm_register(struct request_queue *q, char *disk_name, | 578 | static inline struct nvm_dev *nvm_alloc_dev(int node) |
579 | struct nvm_dev_ops *ops) | 579 | { |
580 | return ERR_PTR(-EINVAL); | ||
581 | } | ||
582 | static inline int nvm_register(struct nvm_dev *dev) | ||
580 | { | 583 | { |
581 | return -EINVAL; | 584 | return -EINVAL; |
582 | } | 585 | } |
583 | static inline void nvm_unregister(char *disk_name) {} | 586 | static inline void nvm_unregister(struct nvm_dev *dev) {} |
584 | #endif /* CONFIG_NVM */ | 587 | #endif /* CONFIG_NVM */ |
585 | #endif /* LIGHTNVM.H */ | 588 | #endif /* LIGHTNVM.H */ |