nvme fixes for Linux 6.9

- Atomic queue limits fixes (Christoph)
  - Fabrics fixes (Hannes, Daniel)
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE3Fbyvv+648XNRdHTPe3zGtjzRgkFAmYOzugACgkQPe3zGtjz
 RglXXw/+NxnIgIP14ZZ0rrg6PoAR7U7RigMvfsqo0oQVK8c889TnzjaTbdJ2dmaV
 7Vo3BfX7Uf7o0e98OwE04CfXNs3VZKd6Q9NCbc4kpNwa5fGSwNx6PcCnYQzPuwZd
 6Irboiozzaq46YTkOWWiGYhAxs22PDOnruX7aPGCwZiGHNAKAnuXfPOL5Oq0G2jX
 1GbEXfgLHQ/bTGNE3Qx1UjaTSbc6sFIKYg1NuWtGGu4HaR5AkGqhR4vuLsBic1cv
 xiwDVTaFTzyXXWq6Hjwti/q7y/OGOaGdP6dHbASCGMmZJfrayZkzEwTejHblS5gp
 h/u2fRBcGCpJ2+nExnUSztrcyfbqnG5MPT4X62eU6KNKjvyo2hXAs4lF2Dl+Ho61
 gzCtyHNPsR1cDU7Y2EPlN0d4ZezZ4DqBQT+1xx60O7/JGyYvyQ36cvRPNKE6Nsc0
 siWLAWPFVPV6RU9trDFAeINLB50fOmHJQR5yWHMs29RLde/LmY9/WeP4Ka/FCbbl
 7qVehD4yqJTTvEZgluD1PvOVaJCPXPKwNhQTatqV8m1PiekFEuA3K5OGZid/VcT9
 RkIQH8HRyACxrcP+oVYXrdiCIqVpE3akT0HXVcmE0Rx1A/vM52qnDS0CZhJFg1SQ
 OKdPGSIfKxFxkCXzl1d2Gx1iIOAeTUhNcNXlSZzEVwa9XSvB0fs=
 =ZgYG
 -----END PGP SIGNATURE-----

Merge tag 'nvme-6.9-2024-04-04' of git://git.infradead.org/nvme into block-6.9

Pull NVMe fixes from Keith:

"nvme fixes for Linux 6.9

 - Atomic queue limits fixes (Christoph)
 - Fabrics fixes (Hannes, Daniel)"

* tag 'nvme-6.9-2024-04-04' of git://git.infradead.org/nvme:
  nvme-fc: rename free_ctrl callback to match name pattern
  nvmet-fc: move RCU read lock to nvmet_fc_assoc_exists
  nvmet: implement unique discovery NQN
  nvme: don't create a multipath node for zero capacity devices
  nvme: split nvme_update_zone_info
  nvme-multipath: don't inherit LBA-related fields for the multipath node
This commit is contained in:
Jens Axboe 2024-04-04 13:23:21 -06:00
commit 9d0e852420
7 changed files with 128 additions and 33 deletions

View File

@ -2076,6 +2076,7 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
bool vwc = ns->ctrl->vwc & NVME_CTRL_VWC_PRESENT;
struct queue_limits lim;
struct nvme_id_ns_nvm *nvm = NULL;
struct nvme_zone_info zi = {};
struct nvme_id_ns *id;
sector_t capacity;
unsigned lbaf;
@ -2088,9 +2089,10 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
if (id->ncap == 0) {
/* namespace not allocated or attached */
info->is_removed = true;
ret = -ENODEV;
ret = -ENXIO;
goto out;
}
lbaf = nvme_lbaf_index(id->flbas);
if (ns->ctrl->ctratt & NVME_CTRL_ATTR_ELBAS) {
ret = nvme_identify_ns_nvm(ns->ctrl, info->nsid, &nvm);
@ -2098,8 +2100,14 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
goto out;
}
if (IS_ENABLED(CONFIG_BLK_DEV_ZONED) &&
ns->head->ids.csi == NVME_CSI_ZNS) {
ret = nvme_query_zone_info(ns, lbaf, &zi);
if (ret < 0)
goto out;
}
blk_mq_freeze_queue(ns->disk->queue);
lbaf = nvme_lbaf_index(id->flbas);
ns->head->lba_shift = id->lbaf[lbaf].ds;
ns->head->nuse = le64_to_cpu(id->nuse);
capacity = nvme_lba_to_sect(ns->head, le64_to_cpu(id->nsze));
@ -2112,13 +2120,8 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
capacity = 0;
nvme_config_discard(ns, &lim);
if (IS_ENABLED(CONFIG_BLK_DEV_ZONED) &&
ns->head->ids.csi == NVME_CSI_ZNS) {
ret = nvme_update_zone_info(ns, lbaf, &lim);
if (ret) {
blk_mq_unfreeze_queue(ns->disk->queue);
goto out;
}
}
ns->head->ids.csi == NVME_CSI_ZNS)
nvme_update_zone_info(ns, &lim, &zi);
ret = queue_limits_commit_update(ns->disk->queue, &lim);
if (ret) {
blk_mq_unfreeze_queue(ns->disk->queue);
@ -2201,6 +2204,7 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)
}
if (!ret && nvme_ns_head_multipath(ns->head)) {
struct queue_limits *ns_lim = &ns->disk->queue->limits;
struct queue_limits lim;
blk_mq_freeze_queue(ns->head->disk->queue);
@ -2212,7 +2216,26 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info)
set_disk_ro(ns->head->disk, nvme_ns_is_readonly(ns, info));
nvme_mpath_revalidate_paths(ns);
/*
* queue_limits mixes values that are the hardware limitations
* for bio splitting with what is the device configuration.
*
* For NVMe the device configuration can change after e.g. a
* Format command, and we really want to pick up the new format
* value here. But we must still stack the queue limits to the
* least common denominator for multipathing to split the bios
* properly.
*
* To work around this, we explicitly set the device
* configuration to those that we just queried, but only stack
* the splitting limits in to make sure we still obey possibly
* lower limitations of other controllers.
*/
lim = queue_limits_start_update(ns->head->disk->queue);
lim.logical_block_size = ns_lim->logical_block_size;
lim.physical_block_size = ns_lim->physical_block_size;
lim.io_min = ns_lim->io_min;
lim.io_opt = ns_lim->io_opt;
queue_limits_stack_bdev(&lim, ns->disk->part0, 0,
ns->head->disk->disk_name);
ret = queue_limits_commit_update(ns->head->disk->queue, &lim);

View File

@ -2428,7 +2428,7 @@ nvme_fc_ctrl_get(struct nvme_fc_ctrl *ctrl)
* controller. Called after last nvme_put_ctrl() call
*/
static void
nvme_fc_nvme_ctrl_freed(struct nvme_ctrl *nctrl)
nvme_fc_free_ctrl(struct nvme_ctrl *nctrl)
{
struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl);
@ -3384,7 +3384,7 @@ static const struct nvme_ctrl_ops nvme_fc_ctrl_ops = {
.reg_read32 = nvmf_reg_read32,
.reg_read64 = nvmf_reg_read64,
.reg_write32 = nvmf_reg_write32,
.free_ctrl = nvme_fc_nvme_ctrl_freed,
.free_ctrl = nvme_fc_free_ctrl,
.submit_async_event = nvme_fc_submit_async_event,
.delete_ctrl = nvme_fc_delete_ctrl,
.get_address = nvmf_get_address,

View File

@ -1036,10 +1036,18 @@ static inline bool nvme_disk_is_ns_head(struct gendisk *disk)
}
#endif /* CONFIG_NVME_MULTIPATH */
struct nvme_zone_info {
u64 zone_size;
unsigned int max_open_zones;
unsigned int max_active_zones;
};
int nvme_ns_report_zones(struct nvme_ns *ns, sector_t sector,
unsigned int nr_zones, report_zones_cb cb, void *data);
int nvme_update_zone_info(struct nvme_ns *ns, unsigned lbaf,
struct queue_limits *lim);
int nvme_query_zone_info(struct nvme_ns *ns, unsigned lbaf,
struct nvme_zone_info *zi);
void nvme_update_zone_info(struct nvme_ns *ns, struct queue_limits *lim,
struct nvme_zone_info *zi);
#ifdef CONFIG_BLK_DEV_ZONED
blk_status_t nvme_setup_zone_mgmt_send(struct nvme_ns *ns, struct request *req,
struct nvme_command *cmnd,

View File

@ -35,8 +35,8 @@ static int nvme_set_max_append(struct nvme_ctrl *ctrl)
return 0;
}
int nvme_update_zone_info(struct nvme_ns *ns, unsigned lbaf,
struct queue_limits *lim)
int nvme_query_zone_info(struct nvme_ns *ns, unsigned lbaf,
struct nvme_zone_info *zi)
{
struct nvme_effects_log *log = ns->head->effects;
struct nvme_command c = { };
@ -89,27 +89,34 @@ int nvme_update_zone_info(struct nvme_ns *ns, unsigned lbaf,
goto free_data;
}
ns->head->zsze =
nvme_lba_to_sect(ns->head, le64_to_cpu(id->lbafe[lbaf].zsze));
if (!is_power_of_2(ns->head->zsze)) {
zi->zone_size = le64_to_cpu(id->lbafe[lbaf].zsze);
if (!is_power_of_2(zi->zone_size)) {
dev_warn(ns->ctrl->device,
"invalid zone size:%llu for namespace:%u\n",
ns->head->zsze, ns->head->ns_id);
"invalid zone size: %llu for namespace: %u\n",
zi->zone_size, ns->head->ns_id);
status = -ENODEV;
goto free_data;
}
zi->max_open_zones = le32_to_cpu(id->mor) + 1;
zi->max_active_zones = le32_to_cpu(id->mar) + 1;
blk_queue_flag_set(QUEUE_FLAG_ZONE_RESETALL, ns->queue);
lim->zoned = 1;
lim->max_open_zones = le32_to_cpu(id->mor) + 1;
lim->max_active_zones = le32_to_cpu(id->mar) + 1;
lim->chunk_sectors = ns->head->zsze;
lim->max_zone_append_sectors = ns->ctrl->max_zone_append;
free_data:
kfree(id);
return status;
}
void nvme_update_zone_info(struct nvme_ns *ns, struct queue_limits *lim,
struct nvme_zone_info *zi)
{
lim->zoned = 1;
lim->max_open_zones = zi->max_open_zones;
lim->max_active_zones = zi->max_active_zones;
lim->max_zone_append_sectors = ns->ctrl->max_zone_append;
lim->chunk_sectors = ns->head->zsze =
nvme_lba_to_sect(ns->head, zi->zone_size);
blk_queue_flag_set(QUEUE_FLAG_ZONE_RESETALL, ns->queue);
}
static void *nvme_zns_alloc_report_buffer(struct nvme_ns *ns,
unsigned int nr_zones, size_t *buflen)
{

View File

@ -1613,6 +1613,11 @@ static struct config_group *nvmet_subsys_make(struct config_group *group,
return ERR_PTR(-EINVAL);
}
if (sysfs_streq(name, nvmet_disc_subsys->subsysnqn)) {
pr_err("can't create subsystem using unique discovery NQN\n");
return ERR_PTR(-EINVAL);
}
subsys = nvmet_subsys_alloc(name, NVME_NQN_NVME);
if (IS_ERR(subsys))
return ERR_CAST(subsys);
@ -2159,7 +2164,49 @@ static const struct config_item_type nvmet_hosts_type = {
static struct config_group nvmet_hosts_group;
static ssize_t nvmet_root_discovery_nqn_show(struct config_item *item,
char *page)
{
return snprintf(page, PAGE_SIZE, "%s\n", nvmet_disc_subsys->subsysnqn);
}
static ssize_t nvmet_root_discovery_nqn_store(struct config_item *item,
const char *page, size_t count)
{
struct list_head *entry;
size_t len;
len = strcspn(page, "\n");
if (!len || len > NVMF_NQN_FIELD_LEN - 1)
return -EINVAL;
down_write(&nvmet_config_sem);
list_for_each(entry, &nvmet_subsystems_group.cg_children) {
struct config_item *item =
container_of(entry, struct config_item, ci_entry);
if (!strncmp(config_item_name(item), page, len)) {
pr_err("duplicate NQN %s\n", config_item_name(item));
up_write(&nvmet_config_sem);
return -EINVAL;
}
}
memset(nvmet_disc_subsys->subsysnqn, 0, NVMF_NQN_FIELD_LEN);
memcpy(nvmet_disc_subsys->subsysnqn, page, len);
up_write(&nvmet_config_sem);
return len;
}
CONFIGFS_ATTR(nvmet_root_, discovery_nqn);
static struct configfs_attribute *nvmet_root_attrs[] = {
&nvmet_root_attr_discovery_nqn,
NULL,
};
static const struct config_item_type nvmet_root_type = {
.ct_attrs = nvmet_root_attrs,
.ct_owner = THIS_MODULE,
};

View File

@ -1541,6 +1541,13 @@ static struct nvmet_subsys *nvmet_find_get_subsys(struct nvmet_port *port,
}
down_read(&nvmet_config_sem);
if (!strncmp(nvmet_disc_subsys->subsysnqn, subsysnqn,
NVMF_NQN_SIZE)) {
if (kref_get_unless_zero(&nvmet_disc_subsys->ref)) {
up_read(&nvmet_config_sem);
return nvmet_disc_subsys;
}
}
list_for_each_entry(p, &port->subsystems, entry) {
if (!strncmp(p->subsys->subsysnqn, subsysnqn,
NVMF_NQN_SIZE)) {

View File

@ -1115,16 +1115,21 @@ nvmet_fc_schedule_delete_assoc(struct nvmet_fc_tgt_assoc *assoc)
}
static bool
nvmet_fc_assoc_exits(struct nvmet_fc_tgtport *tgtport, u64 association_id)
nvmet_fc_assoc_exists(struct nvmet_fc_tgtport *tgtport, u64 association_id)
{
struct nvmet_fc_tgt_assoc *a;
bool found = false;
rcu_read_lock();
list_for_each_entry_rcu(a, &tgtport->assoc_list, a_list) {
if (association_id == a->association_id)
return true;
if (association_id == a->association_id) {
found = true;
break;
}
}
rcu_read_unlock();
return false;
return found;
}
static struct nvmet_fc_tgt_assoc *
@ -1164,13 +1169,11 @@ nvmet_fc_alloc_target_assoc(struct nvmet_fc_tgtport *tgtport, void *hosthandle)
ran = ran << BYTES_FOR_QID_SHIFT;
spin_lock_irqsave(&tgtport->lock, flags);
rcu_read_lock();
if (!nvmet_fc_assoc_exits(tgtport, ran)) {
if (!nvmet_fc_assoc_exists(tgtport, ran)) {
assoc->association_id = ran;
list_add_tail_rcu(&assoc->a_list, &tgtport->assoc_list);
done = true;
}
rcu_read_unlock();
spin_unlock_irqrestore(&tgtport->lock, flags);
} while (!done);