AWUN/NAWUN control the atomicity of command execution in relation to other commands. They impose inter-command serialization of writing of blocks of data to the NVM and prevent blocks of data ending up on the NVM containing partial data from one new command and partial data from one or more other new commands. Parse awun / nawun to verify at least the physical block size exposed is not greater than this value. The special case of awun / nawun == 0xffff tells us we can ramp up to mdts. Suggested-by: Dan Helmick <dan.helmick@xxxxxxxxxxx> Signed-off-by: Luis Chamberlain <mcgrof@xxxxxxxxxx> --- drivers/nvme/host/core.c | 21 +++++++++++++++++++++ drivers/nvme/host/nvme.h | 1 + 2 files changed, 22 insertions(+) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 0365f260c514..7a3c51ac13bd 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1911,6 +1911,7 @@ static void nvme_update_disk_info(struct gendisk *disk, sector_t capacity = nvme_lba_to_sect(ns, le64_to_cpu(id->nsze)); u32 bs = 1U << ns->lba_shift; u32 atomic_bs, phys_bs, io_opt = 0; + u32 awun = 0, awun_bs = 0; if (!nvme_lba_shift_supported(ns)) { capacity = 0; @@ -1931,6 +1932,15 @@ static void nvme_update_disk_info(struct gendisk *disk, atomic_bs = (1 + le16_to_cpu(id->nawupf)) * bs; else atomic_bs = (1 + ns->ctrl->subsys->awupf) * bs; + if (id->nsfeat & NVME_NS_FEAT_ATOMICS && id->nawun) + awun = (1 + le16_to_cpu(id->nawun)); + else + awun = (1 + ns->ctrl->subsys->awun); + /* Indicates MDTS can be used */ + if (awun == 0xffff) + awun_bs = ns->ctrl->max_hw_sectors << SECTOR_SHIFT; + else + awun_bs = awun * bs; } if (id->nsfeat & NVME_NS_FEAT_IO_OPT) { @@ -1940,6 +1950,16 @@ static void nvme_update_disk_info(struct gendisk *disk, io_opt = bs * (1 + le16_to_cpu(id->nows)); } + if (awun) { + phys_bs = min(awun_bs, phys_bs); + + /* + * npwg and nows could be > awun, in such cases users should + * be aware of out of order reads/writes as npwg and nows + * are purely performance optimizations. + */ + } + blk_queue_logical_block_size(disk->queue, bs); /* * Linux filesystems assume writing a single physical block is @@ -2785,6 +2805,7 @@ static int nvme_init_subsystem(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id) kfree(subsys); return -EINVAL; } + subsys->awun = le16_to_cpu(id->awun); subsys->awupf = le16_to_cpu(id->awupf); nvme_mpath_default_iopolicy(subsys); diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index f35647c470af..071ec52d83ea 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -410,6 +410,7 @@ struct nvme_subsystem { u8 cmic; enum nvme_subsys_type subtype; u16 vendor_id; + u16 awun; u16 awupf; /* 0's based awupf value. */ struct ida ns_ida; #ifdef CONFIG_NVME_MULTIPATH -- 2.39.2