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

nvme: LightNVM support

The first generation of Open-Channel SSDs is based on NVMe. The NVMe
driver is extended with support for the LightNVM command set.

Detection is made through PCI IDs. Current supported devices are the
qemu nvme simulator and CNEX Labs Westlake SSD. The qemu nvme enables
support through vendor specific bits in the namespace identification and
the CNEX Labs Westlake SSD implements a LightNVM compatible firmware and
is detected using the same method as qemu.

After detection, vendor specific codes are used to identify the device
and enumerate supported features.
Reviewed-by: default avatarKeith Busch <keith.busch@intel.com>
Signed-off-by: default avatarJavier González <jg@lightnvm.io>
Signed-off-by: default avatarMatias Bjørling <m@bjorling.me>
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
parent ae1519ec
obj-$(CONFIG_BLK_DEV_NVME) += nvme.o obj-$(CONFIG_BLK_DEV_NVME) += nvme.o
nvme-y += pci.o scsi.o nvme-y += pci.o scsi.o lightnvm.o
This diff is collapsed.
...@@ -22,6 +22,11 @@ ...@@ -22,6 +22,11 @@
extern unsigned char nvme_io_timeout; extern unsigned char nvme_io_timeout;
#define NVME_IO_TIMEOUT (nvme_io_timeout * HZ) #define NVME_IO_TIMEOUT (nvme_io_timeout * HZ)
enum {
NVME_NS_LBA = 0,
NVME_NS_LIGHTNVM = 1,
};
/* /*
* Represents an NVM Express device. Each nvme_dev is a PCI function. * Represents an NVM Express device. Each nvme_dev is a PCI function.
*/ */
...@@ -84,6 +89,7 @@ struct nvme_ns { ...@@ -84,6 +89,7 @@ struct nvme_ns {
u16 ms; u16 ms;
bool ext; bool ext;
u8 pi_type; u8 pi_type;
int type;
u64 mode_select_num_blocks; u64 mode_select_num_blocks;
u32 mode_select_block_len; u32 mode_select_block_len;
}; };
...@@ -130,4 +136,8 @@ int nvme_sg_io(struct nvme_ns *ns, struct sg_io_hdr __user *u_hdr); ...@@ -130,4 +136,8 @@ int nvme_sg_io(struct nvme_ns *ns, struct sg_io_hdr __user *u_hdr);
int nvme_sg_io32(struct nvme_ns *ns, unsigned long arg); int nvme_sg_io32(struct nvme_ns *ns, unsigned long arg);
int nvme_sg_get_version_num(int __user *ip); int nvme_sg_get_version_num(int __user *ip);
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);
#endif /* _NVME_H */ #endif /* _NVME_H */
...@@ -1948,6 +1948,9 @@ static void nvme_free_ns(struct kref *kref) ...@@ -1948,6 +1948,9 @@ 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)
nvme_nvm_unregister(ns->queue, ns->disk->disk_name);
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);
...@@ -2017,6 +2020,16 @@ static int nvme_revalidate_disk(struct gendisk *disk) ...@@ -2017,6 +2020,16 @@ static int nvme_revalidate_disk(struct gendisk *disk)
return -ENODEV; return -ENODEV;
} }
if (nvme_nvm_ns_supported(ns, id) && ns->type != NVME_NS_LIGHTNVM) {
if (nvme_nvm_register(ns->queue, disk->disk_name)) {
dev_warn(dev->dev,
"%s: LightNVM init failure\n", __func__);
kfree(id);
return -ENODEV;
}
ns->type = NVME_NS_LIGHTNVM;
}
old_ms = ns->ms; old_ms = ns->ms;
lbaf = id->flbas & NVME_NS_FLBAS_LBA_MASK; lbaf = id->flbas & NVME_NS_FLBAS_LBA_MASK;
ns->lba_shift = id->lbaf[lbaf].ds; ns->lba_shift = id->lbaf[lbaf].ds;
...@@ -2048,7 +2061,9 @@ static int nvme_revalidate_disk(struct gendisk *disk) ...@@ -2048,7 +2061,9 @@ static int nvme_revalidate_disk(struct gendisk *disk)
!ns->ext) !ns->ext)
nvme_init_integrity(ns); nvme_init_integrity(ns);
if (ns->ms && !(ns->ms == 8 && ns->pi_type) && !blk_get_integrity(disk)) if ((ns->ms && !(ns->ms == 8 && ns->pi_type) &&
!blk_get_integrity(disk)) ||
ns->type == NVME_NS_LIGHTNVM)
set_capacity(disk, 0); set_capacity(disk, 0);
else else
set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9)); set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9));
...@@ -2171,17 +2186,19 @@ static void nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid) ...@@ -2171,17 +2186,19 @@ static void nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid)
goto out_free_disk; goto out_free_disk;
kref_get(&dev->kref); kref_get(&dev->kref);
add_disk(ns->disk); if (ns->type != NVME_NS_LIGHTNVM) {
if (ns->ms) { add_disk(ns->disk);
struct block_device *bd = bdget_disk(ns->disk, 0); if (ns->ms) {
if (!bd) struct block_device *bd = bdget_disk(ns->disk, 0);
return; if (!bd)
if (blkdev_get(bd, FMODE_READ, NULL)) { return;
bdput(bd); if (blkdev_get(bd, FMODE_READ, NULL)) {
return; bdput(bd);
return;
}
blkdev_reread_part(bd);
blkdev_put(bd, FMODE_READ);
} }
blkdev_reread_part(bd);
blkdev_put(bd, FMODE_READ);
} }
return; return;
out_free_disk: out_free_disk:
......
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