Commit 818ad0bb authored by Jens Axboe's avatar Jens Axboe
Browse files

Merge tag 'nvme-6.15-2025-04-10' of git://git.infradead.org/nvme into block-6.15

Pull NVMe updates from Christoph:

"nvme updates for Linux 6.15

 - nvmet fc/fcloop refcounting fixes (Daniel Wagner)
 - fix missed namespace/ANA scans (Hannes Reinecke)
 - fix a use after free in the new TCP netns support (Kuniyuki Iwashima)
 - fix a NULL instead of false review in multipath (Uday Shankar)"

* tag 'nvme-6.15-2025-04-10' of git://git.infradead.org/nvme:
  nvmet-fc: put ref when assoc->del_work is already scheduled
  nvmet-fc: take tgtport reference only once
  nvmet-fc: update tgtport ref per assoc
  nvmet-fc: inline nvmet_fc_free_hostport
  nvmet-fc: inline nvmet_fc_delete_assoc
  nvmet-fcloop: add ref counting to lport
  nvmet-fcloop: replace kref with refcount
  nvmet-fcloop: swap list_add_tail arguments
  nvme-tcp: fix use-after-free of netns by kernel TCP socket.
  nvme: multipath: fix return value of nvme_available_path
  nvme: re-read ANA log page after ns scan completes
  nvme: requeue namespace scan on missed AENs
parents 843c6cec 70289ae5
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -4295,6 +4295,15 @@ static void nvme_scan_work(struct work_struct *work)
			nvme_scan_ns_sequential(ctrl);
	}
	mutex_unlock(&ctrl->scan_lock);

	/* Requeue if we have missed AENs */
	if (test_bit(NVME_AER_NOTICE_NS_CHANGED, &ctrl->events))
		nvme_queue_scan(ctrl);
#ifdef CONFIG_NVME_MULTIPATH
	else
		/* Re-read the ANA log page to not miss updates */
		queue_work(nvme_wq, &ctrl->ana_work);
#endif
}

/*
+1 −1
Original line number Diff line number Diff line
@@ -427,7 +427,7 @@ static bool nvme_available_path(struct nvme_ns_head *head)
	struct nvme_ns *ns;

	if (!test_bit(NVME_NSHEAD_DISK_LIVE, &head->flags))
		return NULL;
		return false;

	list_for_each_entry_srcu(ns, &head->list, siblings,
				 srcu_read_lock_held(&head->srcu)) {
+2 −0
Original line number Diff line number Diff line
@@ -1803,6 +1803,8 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid,
		ret = PTR_ERR(sock_file);
		goto err_destroy_mutex;
	}

	sk_net_refcnt_upgrade(queue->sock->sk);
	nvme_tcp_reclassify_socket(queue->sock);

	/* Single syn retry */
+20 −40
Original line number Diff line number Diff line
@@ -995,16 +995,6 @@ nvmet_fc_hostport_get(struct nvmet_fc_hostport *hostport)
	return kref_get_unless_zero(&hostport->ref);
}

static void
nvmet_fc_free_hostport(struct nvmet_fc_hostport *hostport)
{
	/* if LLDD not implemented, leave as NULL */
	if (!hostport || !hostport->hosthandle)
		return;

	nvmet_fc_hostport_put(hostport);
}

static struct nvmet_fc_hostport *
nvmet_fc_match_hostport(struct nvmet_fc_tgtport *tgtport, void *hosthandle)
{
@@ -1028,33 +1018,24 @@ nvmet_fc_alloc_hostport(struct nvmet_fc_tgtport *tgtport, void *hosthandle)
	struct nvmet_fc_hostport *newhost, *match = NULL;
	unsigned long flags;

	/*
	 * Caller holds a reference on tgtport.
	 */

	/* if LLDD not implemented, leave as NULL */
	if (!hosthandle)
		return NULL;

	/*
	 * take reference for what will be the newly allocated hostport if
	 * we end up using a new allocation
	 */
	if (!nvmet_fc_tgtport_get(tgtport))
		return ERR_PTR(-EINVAL);

	spin_lock_irqsave(&tgtport->lock, flags);
	match = nvmet_fc_match_hostport(tgtport, hosthandle);
	spin_unlock_irqrestore(&tgtport->lock, flags);

	if (match) {
		/* no new allocation - release reference */
		nvmet_fc_tgtport_put(tgtport);
	if (match)
		return match;
	}

	newhost = kzalloc(sizeof(*newhost), GFP_KERNEL);
	if (!newhost) {
		/* no new allocation - release reference */
		nvmet_fc_tgtport_put(tgtport);
	if (!newhost)
		return ERR_PTR(-ENOMEM);
	}

	spin_lock_irqsave(&tgtport->lock, flags);
	match = nvmet_fc_match_hostport(tgtport, hosthandle);
@@ -1063,6 +1044,7 @@ nvmet_fc_alloc_hostport(struct nvmet_fc_tgtport *tgtport, void *hosthandle)
		kfree(newhost);
		newhost = match;
	} else {
		nvmet_fc_tgtport_get(tgtport);
		newhost->tgtport = tgtport;
		newhost->hosthandle = hosthandle;
		INIT_LIST_HEAD(&newhost->host_list);
@@ -1075,13 +1057,6 @@ nvmet_fc_alloc_hostport(struct nvmet_fc_tgtport *tgtport, void *hosthandle)
	return newhost;
}

static void
nvmet_fc_delete_assoc(struct nvmet_fc_tgt_assoc *assoc)
{
	nvmet_fc_delete_target_assoc(assoc);
	nvmet_fc_tgt_a_put(assoc);
}

static void
nvmet_fc_delete_assoc_work(struct work_struct *work)
{
@@ -1089,7 +1064,8 @@ nvmet_fc_delete_assoc_work(struct work_struct *work)
		container_of(work, struct nvmet_fc_tgt_assoc, del_work);
	struct nvmet_fc_tgtport *tgtport = assoc->tgtport;

	nvmet_fc_delete_assoc(assoc);
	nvmet_fc_delete_target_assoc(assoc);
	nvmet_fc_tgt_a_put(assoc);
	nvmet_fc_tgtport_put(tgtport);
}

@@ -1097,7 +1073,8 @@ static void
nvmet_fc_schedule_delete_assoc(struct nvmet_fc_tgt_assoc *assoc)
{
	nvmet_fc_tgtport_get(assoc->tgtport);
	queue_work(nvmet_wq, &assoc->del_work);
	if (!queue_work(nvmet_wq, &assoc->del_work))
		nvmet_fc_tgtport_put(assoc->tgtport);
}

static bool
@@ -1143,6 +1120,7 @@ nvmet_fc_alloc_target_assoc(struct nvmet_fc_tgtport *tgtport, void *hosthandle)
		goto out_ida;

	assoc->tgtport = tgtport;
	nvmet_fc_tgtport_get(tgtport);
	assoc->a_id = idx;
	INIT_LIST_HEAD(&assoc->a_list);
	kref_init(&assoc->ref);
@@ -1190,7 +1168,7 @@ nvmet_fc_target_assoc_free(struct kref *ref)
	/* Send Disconnect now that all i/o has completed */
	nvmet_fc_xmt_disconnect_assoc(assoc);

	nvmet_fc_free_hostport(assoc->hostport);
	nvmet_fc_hostport_put(assoc->hostport);
	spin_lock_irqsave(&tgtport->lock, flags);
	oldls = assoc->rcv_disconn;
	spin_unlock_irqrestore(&tgtport->lock, flags);
@@ -1244,6 +1222,8 @@ nvmet_fc_delete_target_assoc(struct nvmet_fc_tgt_assoc *assoc)
	dev_info(tgtport->dev,
		"{%d:%d} Association deleted\n",
		tgtport->fc_target_port.port_num, assoc->a_id);

	nvmet_fc_tgtport_put(tgtport);
}

static struct nvmet_fc_tgt_assoc *
@@ -1455,11 +1435,6 @@ nvmet_fc_free_tgtport(struct kref *ref)
	struct nvmet_fc_tgtport *tgtport =
		container_of(ref, struct nvmet_fc_tgtport, ref);
	struct device *dev = tgtport->dev;
	unsigned long flags;

	spin_lock_irqsave(&nvmet_fc_tgtlock, flags);
	list_del(&tgtport->tgt_list);
	spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags);

	nvmet_fc_free_ls_iodlist(tgtport);

@@ -1620,6 +1595,11 @@ int
nvmet_fc_unregister_targetport(struct nvmet_fc_target_port *target_port)
{
	struct nvmet_fc_tgtport *tgtport = targetport_to_tgtport(target_port);
	unsigned long flags;

	spin_lock_irqsave(&nvmet_fc_tgtlock, flags);
	list_del(&tgtport->tgt_list);
	spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags);

	nvmet_fc_portentry_unbind_tgt(tgtport);

+40 −34
Original line number Diff line number Diff line
@@ -208,6 +208,7 @@ struct fcloop_lport {
	struct nvme_fc_local_port *localport;
	struct list_head lport_list;
	struct completion unreg_done;
	refcount_t ref;
};

struct fcloop_lport_priv {
@@ -239,7 +240,7 @@ struct fcloop_nport {
	struct fcloop_tport *tport;
	struct fcloop_lport *lport;
	struct list_head nport_list;
	struct kref ref;
	refcount_t ref;
	u64 node_name;
	u64 port_name;
	u32 port_role;
@@ -274,7 +275,7 @@ struct fcloop_fcpreq {
	u32				inistate;
	bool				active;
	bool				aborted;
	struct kref			ref;
	refcount_t			ref;
	struct work_struct		fcp_rcv_work;
	struct work_struct		abort_rcv_work;
	struct work_struct		tio_done_work;
@@ -478,7 +479,7 @@ fcloop_t2h_xmt_ls_rsp(struct nvme_fc_local_port *localport,
	if (targetport) {
		tport = targetport->private;
		spin_lock(&tport->lock);
		list_add_tail(&tport->ls_list, &tls_req->ls_list);
		list_add_tail(&tls_req->ls_list, &tport->ls_list);
		spin_unlock(&tport->lock);
		queue_work(nvmet_wq, &tport->ls_work);
	}
@@ -534,24 +535,18 @@ fcloop_tgt_discovery_evt(struct nvmet_fc_target_port *tgtport)
}

static void
fcloop_tfcp_req_free(struct kref *ref)
fcloop_tfcp_req_put(struct fcloop_fcpreq *tfcp_req)
{
	struct fcloop_fcpreq *tfcp_req =
		container_of(ref, struct fcloop_fcpreq, ref);
	if (!refcount_dec_and_test(&tfcp_req->ref))
		return;

	kfree(tfcp_req);
}

static void
fcloop_tfcp_req_put(struct fcloop_fcpreq *tfcp_req)
{
	kref_put(&tfcp_req->ref, fcloop_tfcp_req_free);
}

static int
fcloop_tfcp_req_get(struct fcloop_fcpreq *tfcp_req)
{
	return kref_get_unless_zero(&tfcp_req->ref);
	return refcount_inc_not_zero(&tfcp_req->ref);
}

static void
@@ -748,7 +743,7 @@ fcloop_fcp_req(struct nvme_fc_local_port *localport,
	INIT_WORK(&tfcp_req->fcp_rcv_work, fcloop_fcp_recv_work);
	INIT_WORK(&tfcp_req->abort_rcv_work, fcloop_fcp_abort_recv_work);
	INIT_WORK(&tfcp_req->tio_done_work, fcloop_tgt_fcprqst_done_work);
	kref_init(&tfcp_req->ref);
	refcount_set(&tfcp_req->ref, 1);

	queue_work(nvmet_wq, &tfcp_req->fcp_rcv_work);

@@ -1001,24 +996,39 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport,
}

static void
fcloop_nport_free(struct kref *ref)
fcloop_lport_put(struct fcloop_lport *lport)
{
	struct fcloop_nport *nport =
		container_of(ref, struct fcloop_nport, ref);
	unsigned long flags;

	kfree(nport);
	if (!refcount_dec_and_test(&lport->ref))
		return;

	spin_lock_irqsave(&fcloop_lock, flags);
	list_del(&lport->lport_list);
	spin_unlock_irqrestore(&fcloop_lock, flags);

	kfree(lport);
}

static int
fcloop_lport_get(struct fcloop_lport *lport)
{
	return refcount_inc_not_zero(&lport->ref);
}

static void
fcloop_nport_put(struct fcloop_nport *nport)
{
	kref_put(&nport->ref, fcloop_nport_free);
	if (!refcount_dec_and_test(&nport->ref))
		return;

	kfree(nport);
}

static int
fcloop_nport_get(struct fcloop_nport *nport)
{
	return kref_get_unless_zero(&nport->ref);
	return refcount_inc_not_zero(&nport->ref);
}

static void
@@ -1029,6 +1039,8 @@ fcloop_localport_delete(struct nvme_fc_local_port *localport)

	/* release any threads waiting for the unreg to complete */
	complete(&lport->unreg_done);

	fcloop_lport_put(lport);
}

static void
@@ -1140,6 +1152,7 @@ fcloop_create_local_port(struct device *dev, struct device_attribute *attr,

		lport->localport = localport;
		INIT_LIST_HEAD(&lport->lport_list);
		refcount_set(&lport->ref, 1);

		spin_lock_irqsave(&fcloop_lock, flags);
		list_add_tail(&lport->lport_list, &fcloop_lports);
@@ -1156,13 +1169,6 @@ fcloop_create_local_port(struct device *dev, struct device_attribute *attr,
	return ret ? ret : count;
}


static void
__unlink_local_port(struct fcloop_lport *lport)
{
	list_del(&lport->lport_list);
}

static int
__wait_localport_unreg(struct fcloop_lport *lport)
{
@@ -1175,8 +1181,6 @@ __wait_localport_unreg(struct fcloop_lport *lport)
	if (!ret)
		wait_for_completion(&lport->unreg_done);

	kfree(lport);

	return ret;
}

@@ -1199,8 +1203,9 @@ fcloop_delete_local_port(struct device *dev, struct device_attribute *attr,
	list_for_each_entry(tlport, &fcloop_lports, lport_list) {
		if (tlport->localport->node_name == nodename &&
		    tlport->localport->port_name == portname) {
			if (!fcloop_lport_get(tlport))
				break;
			lport = tlport;
			__unlink_local_port(lport);
			break;
		}
	}
@@ -1210,6 +1215,7 @@ fcloop_delete_local_port(struct device *dev, struct device_attribute *attr,
		return -ENOENT;

	ret = __wait_localport_unreg(lport);
	fcloop_lport_put(lport);

	return ret ? ret : count;
}
@@ -1249,7 +1255,7 @@ fcloop_alloc_nport(const char *buf, size_t count, bool remoteport)
		newnport->port_role = opts->roles;
	if (opts->mask & NVMF_OPT_FCADDR)
		newnport->port_id = opts->fcaddr;
	kref_init(&newnport->ref);
	refcount_set(&newnport->ref, 1);

	spin_lock_irqsave(&fcloop_lock, flags);

@@ -1637,17 +1643,17 @@ static void __exit fcloop_exit(void)
	for (;;) {
		lport = list_first_entry_or_null(&fcloop_lports,
						typeof(*lport), lport_list);
		if (!lport)
		if (!lport || !fcloop_lport_get(lport))
			break;

		__unlink_local_port(lport);

		spin_unlock_irqrestore(&fcloop_lock, flags);

		ret = __wait_localport_unreg(lport);
		if (ret)
			pr_warn("%s: Failed deleting local port\n", __func__);

		fcloop_lport_put(lport);

		spin_lock_irqsave(&fcloop_lock, flags);
	}