* [PATCH v5 01/14] nvmet-fcloop: track ref counts for nports
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-04-24 10:08 ` Hannes Reinecke
2025-04-23 13:21 ` [PATCH v5 02/14] nvmet-fcloop: remove nport from list on last user Daniel Wagner
` (13 subsequent siblings)
14 siblings, 1 reply; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
A nport object is always used in association with targerport,
remoteport, tport and rport objects. Add explicit references for any of
the associated object. This ensures that nport is not removed too early
on shutdown sequences.
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/target/fcloop.c | 133 +++++++++++++++++++++++++++++--------------
1 file changed, 90 insertions(+), 43 deletions(-)
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index 641201e62c1bafa13986642c6c4067b35f784edd..2b23e43ef4403fa4d70c66263f7750165d2ddc72 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -1047,8 +1047,14 @@ static void
fcloop_remoteport_delete(struct nvme_fc_remote_port *remoteport)
{
struct fcloop_rport *rport = remoteport->private;
+ unsigned long flags;
flush_work(&rport->ls_work);
+
+ spin_lock_irqsave(&fcloop_lock, flags);
+ rport->nport->rport = NULL;
+ spin_unlock_irqrestore(&fcloop_lock, flags);
+
fcloop_nport_put(rport->nport);
}
@@ -1056,8 +1062,14 @@ static void
fcloop_targetport_delete(struct nvmet_fc_target_port *targetport)
{
struct fcloop_tport *tport = targetport->private;
+ unsigned long flags;
flush_work(&tport->ls_work);
+
+ spin_lock_irqsave(&fcloop_lock, flags);
+ tport->nport->tport = NULL;
+ spin_unlock_irqrestore(&fcloop_lock, flags);
+
fcloop_nport_put(tport->nport);
}
@@ -1184,6 +1196,37 @@ __wait_localport_unreg(struct fcloop_lport *lport)
return ret;
}
+static struct fcloop_nport *
+__fcloop_nport_lookup(u64 node_name, u64 port_name)
+{
+ struct fcloop_nport *nport;
+
+ list_for_each_entry(nport, &fcloop_nports, nport_list) {
+ if (nport->node_name != node_name ||
+ nport->port_name != port_name)
+ continue;
+
+ if (fcloop_nport_get(nport))
+ return nport;
+
+ break;
+ }
+
+ return NULL;
+}
+
+static struct fcloop_nport *
+fcloop_nport_lookup(u64 node_name, u64 port_name)
+{
+ struct fcloop_nport *nport;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fcloop_lock, flags);
+ nport = __fcloop_nport_lookup(node_name, port_name);
+ spin_unlock_irqrestore(&fcloop_lock, flags);
+
+ return nport;
+}
static ssize_t
fcloop_delete_local_port(struct device *dev, struct device_attribute *attr,
@@ -1365,6 +1408,8 @@ __unlink_remote_port(struct fcloop_nport *nport)
{
struct fcloop_rport *rport = nport->rport;
+ lockdep_assert_held(&fcloop_lock);
+
if (rport && nport->tport)
nport->tport->remoteport = NULL;
nport->rport = NULL;
@@ -1377,9 +1422,6 @@ __unlink_remote_port(struct fcloop_nport *nport)
static int
__remoteport_unreg(struct fcloop_nport *nport, struct fcloop_rport *rport)
{
- if (!rport)
- return -EALREADY;
-
return nvme_fc_unregister_remoteport(rport->remoteport);
}
@@ -1387,8 +1429,8 @@ static ssize_t
fcloop_delete_remote_port(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct fcloop_nport *nport = NULL, *tmpport;
- static struct fcloop_rport *rport;
+ struct fcloop_nport *nport;
+ struct fcloop_rport *rport;
u64 nodename, portname;
unsigned long flags;
int ret;
@@ -1397,24 +1439,24 @@ fcloop_delete_remote_port(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
- spin_lock_irqsave(&fcloop_lock, flags);
-
- list_for_each_entry(tmpport, &fcloop_nports, nport_list) {
- if (tmpport->node_name == nodename &&
- tmpport->port_name == portname && tmpport->rport) {
- nport = tmpport;
- rport = __unlink_remote_port(nport);
- break;
- }
- }
+ nport = fcloop_nport_lookup(nodename, portname);
+ if (!nport)
+ return -ENOENT;
+ spin_lock_irqsave(&fcloop_lock, flags);
+ rport = __unlink_remote_port(nport);
spin_unlock_irqrestore(&fcloop_lock, flags);
- if (!nport)
- return -ENOENT;
+ if (!rport) {
+ ret = -ENOENT;
+ goto out_nport_put;
+ }
ret = __remoteport_unreg(nport, rport);
+out_nport_put:
+ fcloop_nport_put(nport);
+
return ret ? ret : count;
}
@@ -1465,6 +1507,8 @@ __unlink_target_port(struct fcloop_nport *nport)
{
struct fcloop_tport *tport = nport->tport;
+ lockdep_assert_held(&fcloop_lock);
+
if (tport && nport->rport)
nport->rport->targetport = NULL;
nport->tport = NULL;
@@ -1475,9 +1519,6 @@ __unlink_target_port(struct fcloop_nport *nport)
static int
__targetport_unreg(struct fcloop_nport *nport, struct fcloop_tport *tport)
{
- if (!tport)
- return -EALREADY;
-
return nvmet_fc_unregister_targetport(tport->targetport);
}
@@ -1485,8 +1526,8 @@ static ssize_t
fcloop_delete_target_port(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct fcloop_nport *nport = NULL, *tmpport;
- struct fcloop_tport *tport = NULL;
+ struct fcloop_nport *nport;
+ struct fcloop_tport *tport;
u64 nodename, portname;
unsigned long flags;
int ret;
@@ -1495,24 +1536,24 @@ fcloop_delete_target_port(struct device *dev, struct device_attribute *attr,
if (ret)
return ret;
- spin_lock_irqsave(&fcloop_lock, flags);
-
- list_for_each_entry(tmpport, &fcloop_nports, nport_list) {
- if (tmpport->node_name == nodename &&
- tmpport->port_name == portname && tmpport->tport) {
- nport = tmpport;
- tport = __unlink_target_port(nport);
- break;
- }
- }
+ nport = fcloop_nport_lookup(nodename, portname);
+ if (!nport)
+ return -ENOENT;
+ spin_lock_irqsave(&fcloop_lock, flags);
+ tport = __unlink_target_port(nport);
spin_unlock_irqrestore(&fcloop_lock, flags);
- if (!nport)
- return -ENOENT;
+ if (!tport) {
+ ret = -ENOENT;
+ goto out_nport_put;
+ }
ret = __targetport_unreg(nport, tport);
+out_nport_put:
+ fcloop_nport_put(nport);
+
return ret ? ret : count;
}
@@ -1609,8 +1650,8 @@ static int __init fcloop_init(void)
static void __exit fcloop_exit(void)
{
- struct fcloop_lport *lport = NULL;
- struct fcloop_nport *nport = NULL;
+ struct fcloop_lport *lport;
+ struct fcloop_nport *nport;
struct fcloop_tport *tport;
struct fcloop_rport *rport;
unsigned long flags;
@@ -1621,7 +1662,7 @@ static void __exit fcloop_exit(void)
for (;;) {
nport = list_first_entry_or_null(&fcloop_nports,
typeof(*nport), nport_list);
- if (!nport)
+ if (!nport || !fcloop_nport_get(nport))
break;
tport = __unlink_target_port(nport);
@@ -1629,13 +1670,19 @@ static void __exit fcloop_exit(void)
spin_unlock_irqrestore(&fcloop_lock, flags);
- ret = __targetport_unreg(nport, tport);
- if (ret)
- pr_warn("%s: Failed deleting target port\n", __func__);
+ if (tport) {
+ ret = __targetport_unreg(nport, tport);
+ if (ret)
+ pr_warn("%s: Failed deleting target port\n", __func__);
+ }
- ret = __remoteport_unreg(nport, rport);
- if (ret)
- pr_warn("%s: Failed deleting remote port\n", __func__);
+ if (rport) {
+ ret = __remoteport_unreg(nport, rport);
+ if (ret)
+ pr_warn("%s: Failed deleting remote port\n", __func__);
+ }
+
+ fcloop_nport_put(nport);
spin_lock_irqsave(&fcloop_lock, flags);
}
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* Re: [PATCH v5 01/14] nvmet-fcloop: track ref counts for nports
2025-04-23 13:21 ` [PATCH v5 01/14] nvmet-fcloop: track ref counts for nports Daniel Wagner
@ 2025-04-24 10:08 ` Hannes Reinecke
2025-04-24 11:13 ` Daniel Wagner
0 siblings, 1 reply; 34+ messages in thread
From: Hannes Reinecke @ 2025-04-24 10:08 UTC (permalink / raw)
To: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni
Cc: Keith Busch, linux-nvme, linux-kernel
On 4/23/25 15:21, Daniel Wagner wrote:
> A nport object is always used in association with targerport,
> remoteport, tport and rport objects. Add explicit references for any of
> the associated object. This ensures that nport is not removed too early
> on shutdown sequences.
>
> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> ---
> drivers/nvme/target/fcloop.c | 133 +++++++++++++++++++++++++++++--------------
> 1 file changed, 90 insertions(+), 43 deletions(-)
>
> diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
> index 641201e62c1bafa13986642c6c4067b35f784edd..2b23e43ef4403fa4d70c66263f7750165d2ddc72 100644
> --- a/drivers/nvme/target/fcloop.c
> +++ b/drivers/nvme/target/fcloop.c
> @@ -1047,8 +1047,14 @@ static void
> fcloop_remoteport_delete(struct nvme_fc_remote_port *remoteport)
> {
> struct fcloop_rport *rport = remoteport->private;
> + unsigned long flags;
>
> flush_work(&rport->ls_work);
> +
> + spin_lock_irqsave(&fcloop_lock, flags);
> + rport->nport->rport = NULL;
> + spin_unlock_irqrestore(&fcloop_lock, flags);
> +
> fcloop_nport_put(rport->nport);
> }
>
> @@ -1056,8 +1062,14 @@ static void
> fcloop_targetport_delete(struct nvmet_fc_target_port *targetport)
> {
> struct fcloop_tport *tport = targetport->private;
> + unsigned long flags;
>
> flush_work(&tport->ls_work);
> +
> + spin_lock_irqsave(&fcloop_lock, flags);
> + tport->nport->tport = NULL;
> + spin_unlock_irqrestore(&fcloop_lock, flags);
> +
> fcloop_nport_put(tport->nport);
> }
>
> @@ -1184,6 +1196,37 @@ __wait_localport_unreg(struct fcloop_lport *lport)
> return ret;
> }
>
> +static struct fcloop_nport *
> +__fcloop_nport_lookup(u64 node_name, u64 port_name)
> +{
> + struct fcloop_nport *nport;
> +
> + list_for_each_entry(nport, &fcloop_nports, nport_list) {
> + if (nport->node_name != node_name ||
> + nport->port_name != port_name)
> + continue;
> +
> + if (fcloop_nport_get(nport))
> + return nport;
> +
> + break;
> + }
> +
> + return NULL;
> +}
> +
> +static struct fcloop_nport *
> +fcloop_nport_lookup(u64 node_name, u64 port_name)
> +{
> + struct fcloop_nport *nport;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&fcloop_lock, flags);
> + nport = __fcloop_nport_lookup(node_name, port_name);
> + spin_unlock_irqrestore(&fcloop_lock, flags);
> +
> + return nport;
> +}
>
> static ssize_t
> fcloop_delete_local_port(struct device *dev, struct device_attribute *attr,
> @@ -1365,6 +1408,8 @@ __unlink_remote_port(struct fcloop_nport *nport)
> {
> struct fcloop_rport *rport = nport->rport;
>
> + lockdep_assert_held(&fcloop_lock);
> +
> if (rport && nport->tport)
> nport->tport->remoteport = NULL;
> nport->rport = NULL;
> @@ -1377,9 +1422,6 @@ __unlink_remote_port(struct fcloop_nport *nport)
> static int
> __remoteport_unreg(struct fcloop_nport *nport, struct fcloop_rport *rport)
> {
> - if (!rport)
> - return -EALREADY;
> -
> return nvme_fc_unregister_remoteport(rport->remoteport);
> }
>
> @@ -1387,8 +1429,8 @@ static ssize_t
> fcloop_delete_remote_port(struct device *dev, struct device_attribute *attr,
> const char *buf, size_t count)
> {
> - struct fcloop_nport *nport = NULL, *tmpport;
> - static struct fcloop_rport *rport;
> + struct fcloop_nport *nport;
> + struct fcloop_rport *rport;
> u64 nodename, portname;
> unsigned long flags;
> int ret;
> @@ -1397,24 +1439,24 @@ fcloop_delete_remote_port(struct device *dev, struct device_attribute *attr,
> if (ret)
> return ret;
>
> - spin_lock_irqsave(&fcloop_lock, flags);
> -
> - list_for_each_entry(tmpport, &fcloop_nports, nport_list) {
> - if (tmpport->node_name == nodename &&
> - tmpport->port_name == portname && tmpport->rport) {
> - nport = tmpport;
> - rport = __unlink_remote_port(nport);
> - break;
> - }
> - }
> + nport = fcloop_nport_lookup(nodename, portname);
> + if (!nport)
> + return -ENOENT;
>
> + spin_lock_irqsave(&fcloop_lock, flags);
> + rport = __unlink_remote_port(nport);
> spin_unlock_irqrestore(&fcloop_lock, flags);
>
> - if (!nport)
> - return -ENOENT;
> + if (!rport) {
> + ret = -ENOENT;
> + goto out_nport_put;
> + }
>
> ret = __remoteport_unreg(nport, rport);
>
> +out_nport_put:
> + fcloop_nport_put(nport);
> +
> return ret ? ret : count;
> }
>
> @@ -1465,6 +1507,8 @@ __unlink_target_port(struct fcloop_nport *nport)
> {
> struct fcloop_tport *tport = nport->tport;
>
> + lockdep_assert_held(&fcloop_lock);
> +
> if (tport && nport->rport)
> nport->rport->targetport = NULL;
> nport->tport = NULL;
> @@ -1475,9 +1519,6 @@ __unlink_target_port(struct fcloop_nport *nport)
> static int
> __targetport_unreg(struct fcloop_nport *nport, struct fcloop_tport *tport)
> {
> - if (!tport)
> - return -EALREADY;
> -
> return nvmet_fc_unregister_targetport(tport->targetport);
> }
>
> @@ -1485,8 +1526,8 @@ static ssize_t
> fcloop_delete_target_port(struct device *dev, struct device_attribute *attr,
> const char *buf, size_t count)
> {
> - struct fcloop_nport *nport = NULL, *tmpport;
> - struct fcloop_tport *tport = NULL;
> + struct fcloop_nport *nport;
> + struct fcloop_tport *tport;
> u64 nodename, portname;
> unsigned long flags;
> int ret;
> @@ -1495,24 +1536,24 @@ fcloop_delete_target_port(struct device *dev, struct device_attribute *attr,
> if (ret)
> return ret;
>
> - spin_lock_irqsave(&fcloop_lock, flags);
> -
> - list_for_each_entry(tmpport, &fcloop_nports, nport_list) {
> - if (tmpport->node_name == nodename &&
> - tmpport->port_name == portname && tmpport->tport) {
> - nport = tmpport;
> - tport = __unlink_target_port(nport);
> - break;
> - }
> - }
> + nport = fcloop_nport_lookup(nodename, portname);
> + if (!nport)
> + return -ENOENT;
>
> + spin_lock_irqsave(&fcloop_lock, flags);
> + tport = __unlink_target_port(nport);
> spin_unlock_irqrestore(&fcloop_lock, flags);
>
Hmm. This now has a race condition; we're taking the lock
during lokup, drop the lock, take the lock again, and unlink
the port.
Please do a __fcloop_nport_lookup() function which doesn't
take a lock and avoid this race.
> - if (!nport)
> - return -ENOENT;
> + if (!tport) {
> + ret = -ENOENT;
> + goto out_nport_put;
> + }
>
> ret = __targetport_unreg(nport, tport);
>
> +out_nport_put:
> + fcloop_nport_put(nport);
> +
> return ret ? ret : count;
> }
>
> @@ -1609,8 +1650,8 @@ static int __init fcloop_init(void)
>
> static void __exit fcloop_exit(void)
> {
> - struct fcloop_lport *lport = NULL;
> - struct fcloop_nport *nport = NULL;
> + struct fcloop_lport *lport;
> + struct fcloop_nport *nport;
> struct fcloop_tport *tport;
> struct fcloop_rport *rport;
> unsigned long flags;
> @@ -1621,7 +1662,7 @@ static void __exit fcloop_exit(void)
> for (;;) {
> nport = list_first_entry_or_null(&fcloop_nports,
> typeof(*nport), nport_list);
> - if (!nport)
> + if (!nport || !fcloop_nport_get(nport))
> break;
>
> tport = __unlink_target_port(nport);
> @@ -1629,13 +1670,19 @@ static void __exit fcloop_exit(void)
>
> spin_unlock_irqrestore(&fcloop_lock, flags);
>
> - ret = __targetport_unreg(nport, tport);
> - if (ret)
> - pr_warn("%s: Failed deleting target port\n", __func__);
> + if (tport) {
> + ret = __targetport_unreg(nport, tport);
> + if (ret)
> + pr_warn("%s: Failed deleting target port\n", __func__);
> + }
>
And same here; don't drop the lock after lookup.
> - ret = __remoteport_unreg(nport, rport);
> - if (ret)
> - pr_warn("%s: Failed deleting remote port\n", __func__);
> + if (rport) {
> + ret = __remoteport_unreg(nport, rport);
> + if (ret)
> + pr_warn("%s: Failed deleting remote port\n", __func__);
> + }
> +
> + fcloop_nport_put(nport);
>
> spin_lock_irqsave(&fcloop_lock, flags);
> }
>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PATCH v5 01/14] nvmet-fcloop: track ref counts for nports
2025-04-24 10:08 ` Hannes Reinecke
@ 2025-04-24 11:13 ` Daniel Wagner
0 siblings, 0 replies; 34+ messages in thread
From: Daniel Wagner @ 2025-04-24 11:13 UTC (permalink / raw)
To: Hannes Reinecke
Cc: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Keith Busch, linux-nvme, linux-kernel
On Thu, Apr 24, 2025 at 12:08:33PM +0200, Hannes Reinecke wrote:
> > + nport = fcloop_nport_lookup(nodename, portname);
> > + if (!nport)
> > + return -ENOENT;
> > + spin_lock_irqsave(&fcloop_lock, flags);
> > + tport = __unlink_target_port(nport);
> > spin_unlock_irqrestore(&fcloop_lock, flags);
> Hmm. This now has a race condition; we're taking the lock
> during lokup, drop the lock, take the lock again, and unlink
> the port.
> Please do a __fcloop_nport_lookup() function which doesn't
> take a lock and avoid this race.
The lock is protecting the list iteration not the object itself. The
lookup function will increase the refcount, so it doesn't get freed
after the unlock. How is this racing?
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v5 02/14] nvmet-fcloop: remove nport from list on last user
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
2025-04-23 13:21 ` [PATCH v5 01/14] nvmet-fcloop: track ref counts for nports Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-04-23 13:21 ` [PATCH v5 03/14] nvmet-fcloop: refactor fcloop_nport_alloc and track lport Daniel Wagner
` (12 subsequent siblings)
14 siblings, 0 replies; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
The nport object has an association with the rport and lport object,
that means we can only remove an nport object from the global nport_list
after the last user of an rport or lport is gone.
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/target/fcloop.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index 2b23e43ef4403fa4d70c66263f7750165d2ddc72..2cce7649af276528360395b6d58f03183c11da20 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -1019,9 +1019,15 @@ fcloop_lport_get(struct fcloop_lport *lport)
static void
fcloop_nport_put(struct fcloop_nport *nport)
{
+ unsigned long flags;
+
if (!refcount_dec_and_test(&nport->ref))
return;
+ spin_lock_irqsave(&fcloop_lock, flags);
+ list_del(&nport->nport_list);
+ spin_unlock_irqrestore(&fcloop_lock, flags);
+
kfree(nport);
}
@@ -1414,8 +1420,6 @@ __unlink_remote_port(struct fcloop_nport *nport)
nport->tport->remoteport = NULL;
nport->rport = NULL;
- list_del(&nport->nport_list);
-
return rport;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PATCH v5 03/14] nvmet-fcloop: refactor fcloop_nport_alloc and track lport
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
2025-04-23 13:21 ` [PATCH v5 01/14] nvmet-fcloop: track ref counts for nports Daniel Wagner
2025-04-23 13:21 ` [PATCH v5 02/14] nvmet-fcloop: remove nport from list on last user Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-04-24 10:09 ` Hannes Reinecke
2025-04-23 13:21 ` [PATCH v5 04/14] nvmet-fcloop: refactor fcloop_delete_local_port Daniel Wagner
` (11 subsequent siblings)
14 siblings, 1 reply; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
The checks for a valid input values are mixed with the logic to insert a
newly allocated nport. Refactor the function so that first the checks
are done.
This allows to untangle the setup steps into a more linear form which
reduces the complexity of the functions.
Also start tracking lport when a lport is assigned to a nport. This
ensures, that the lport is not going away as long it is still referenced
by a nport.
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/target/fcloop.c | 107 ++++++++++++++++++++++++++-----------------
1 file changed, 64 insertions(+), 43 deletions(-)
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index 2cce7649af276528360395b6d58f03183c11da20..a8134127ad8087190d674251a88545da3f8800d7 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -1028,6 +1028,9 @@ fcloop_nport_put(struct fcloop_nport *nport)
list_del(&nport->nport_list);
spin_unlock_irqrestore(&fcloop_lock, flags);
+ if (nport->lport)
+ fcloop_lport_put(nport->lport);
+
kfree(nport);
}
@@ -1234,6 +1237,25 @@ fcloop_nport_lookup(u64 node_name, u64 port_name)
return nport;
}
+static struct fcloop_lport *
+__fcloop_lport_lookup(u64 node_name, u64 port_name)
+{
+ struct fcloop_lport *lport;
+
+ list_for_each_entry(lport, &fcloop_lports, lport_list) {
+ if (lport->localport->node_name != node_name ||
+ lport->localport->port_name != port_name)
+ continue;
+
+ if (fcloop_lport_get(lport))
+ return lport;
+
+ break;
+ }
+
+ return NULL;
+}
+
static ssize_t
fcloop_delete_local_port(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
@@ -1272,8 +1294,8 @@ fcloop_delete_local_port(struct device *dev, struct device_attribute *attr,
static struct fcloop_nport *
fcloop_alloc_nport(const char *buf, size_t count, bool remoteport)
{
- struct fcloop_nport *newnport, *nport = NULL;
- struct fcloop_lport *tmplport, *lport = NULL;
+ struct fcloop_nport *newnport, *nport;
+ struct fcloop_lport *lport;
struct fcloop_ctrl_options *opts;
unsigned long flags;
u32 opts_mask = (remoteport) ? RPORT_OPTS : TGTPORT_OPTS;
@@ -1288,10 +1310,8 @@ fcloop_alloc_nport(const char *buf, size_t count, bool remoteport)
goto out_free_opts;
/* everything there ? */
- if ((opts->mask & opts_mask) != opts_mask) {
- ret = -EINVAL;
+ if ((opts->mask & opts_mask) != opts_mask)
goto out_free_opts;
- }
newnport = kzalloc(sizeof(*newnport), GFP_KERNEL);
if (!newnport)
@@ -1307,60 +1327,61 @@ fcloop_alloc_nport(const char *buf, size_t count, bool remoteport)
refcount_set(&newnport->ref, 1);
spin_lock_irqsave(&fcloop_lock, flags);
-
- list_for_each_entry(tmplport, &fcloop_lports, lport_list) {
- if (tmplport->localport->node_name == opts->wwnn &&
- tmplport->localport->port_name == opts->wwpn)
- goto out_invalid_opts;
-
- if (tmplport->localport->node_name == opts->lpwwnn &&
- tmplport->localport->port_name == opts->lpwwpn)
- lport = tmplport;
+ lport = __fcloop_lport_lookup(opts->wwnn, opts->wwpn);
+ if (lport) {
+ /* invalid configuration */
+ fcloop_lport_put(lport);
+ goto out_free_newnport;
}
if (remoteport) {
- if (!lport)
- goto out_invalid_opts;
- newnport->lport = lport;
- }
-
- list_for_each_entry(nport, &fcloop_nports, nport_list) {
- if (nport->node_name == opts->wwnn &&
- nport->port_name == opts->wwpn) {
- if ((remoteport && nport->rport) ||
- (!remoteport && nport->tport)) {
- nport = NULL;
- goto out_invalid_opts;
- }
-
- fcloop_nport_get(nport);
-
- spin_unlock_irqrestore(&fcloop_lock, flags);
-
- if (remoteport)
- nport->lport = lport;
- if (opts->mask & NVMF_OPT_ROLES)
- nport->port_role = opts->roles;
- if (opts->mask & NVMF_OPT_FCADDR)
- nport->port_id = opts->fcaddr;
+ lport = __fcloop_lport_lookup(opts->lpwwnn, opts->lpwwpn);
+ if (!lport) {
+ /* invalid configuration */
goto out_free_newnport;
}
}
- list_add_tail(&newnport->nport_list, &fcloop_nports);
+ nport = __fcloop_nport_lookup(opts->wwnn, opts->wwpn);
+ if (nport) {
+ if ((remoteport && nport->rport) ||
+ (!remoteport && nport->tport)) {
+ /* invalid configuration */
+ goto out_put_nport;
+ }
+ /* found existing nport, discard the new nport */
+ kfree(newnport);
+ } else {
+ list_add_tail(&newnport->nport_list, &fcloop_nports);
+ nport = newnport;
+ }
+
+ if (opts->mask & NVMF_OPT_ROLES)
+ nport->port_role = opts->roles;
+ if (opts->mask & NVMF_OPT_FCADDR)
+ nport->port_id = opts->fcaddr;
+ if (lport) {
+ if (!nport->lport)
+ nport->lport = lport;
+ else
+ fcloop_lport_put(lport);
+ }
spin_unlock_irqrestore(&fcloop_lock, flags);
kfree(opts);
- return newnport;
+ return nport;
-out_invalid_opts:
- spin_unlock_irqrestore(&fcloop_lock, flags);
+out_put_nport:
+ if (lport)
+ fcloop_lport_put(lport);
+ fcloop_nport_put(nport);
out_free_newnport:
+ spin_unlock_irqrestore(&fcloop_lock, flags);
kfree(newnport);
out_free_opts:
kfree(opts);
- return nport;
+ return NULL;
}
static ssize_t
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* Re: [PATCH v5 03/14] nvmet-fcloop: refactor fcloop_nport_alloc and track lport
2025-04-23 13:21 ` [PATCH v5 03/14] nvmet-fcloop: refactor fcloop_nport_alloc and track lport Daniel Wagner
@ 2025-04-24 10:09 ` Hannes Reinecke
0 siblings, 0 replies; 34+ messages in thread
From: Hannes Reinecke @ 2025-04-24 10:09 UTC (permalink / raw)
To: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni
Cc: Keith Busch, linux-nvme, linux-kernel
On 4/23/25 15:21, Daniel Wagner wrote:
> The checks for a valid input values are mixed with the logic to insert a
> newly allocated nport. Refactor the function so that first the checks
> are done.
>
> This allows to untangle the setup steps into a more linear form which
> reduces the complexity of the functions.
>
> Also start tracking lport when a lport is assigned to a nport. This
> ensures, that the lport is not going away as long it is still referenced
> by a nport.
>
> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> ---
> drivers/nvme/target/fcloop.c | 107 ++++++++++++++++++++++++++-----------------
> 1 file changed, 64 insertions(+), 43 deletions(-)
>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v5 04/14] nvmet-fcloop: refactor fcloop_delete_local_port
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
` (2 preceding siblings ...)
2025-04-23 13:21 ` [PATCH v5 03/14] nvmet-fcloop: refactor fcloop_nport_alloc and track lport Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-04-24 10:10 ` Hannes Reinecke
2025-04-23 13:21 ` [PATCH v5 05/14] nvmet-fcloop: update refs on tfcp_req Daniel Wagner
` (10 subsequent siblings)
14 siblings, 1 reply; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
Use the newly introduced fcloop_lport_lookup instead
of the open coded version.
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/target/fcloop.c | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index a8134127ad8087190d674251a88545da3f8800d7..1a8ae33c1699be3b7a2a7170dff77e324c127ebb 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -1256,32 +1256,32 @@ __fcloop_lport_lookup(u64 node_name, u64 port_name)
return NULL;
}
+static struct fcloop_lport *
+fcloop_lport_lookup(u64 node_name, u64 port_name)
+{
+ struct fcloop_lport *lport;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fcloop_lock, flags);
+ lport = __fcloop_lport_lookup(node_name, port_name);
+ spin_unlock_irqrestore(&fcloop_lock, flags);
+
+ return lport;
+}
+
static ssize_t
fcloop_delete_local_port(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct fcloop_lport *tlport, *lport = NULL;
+ struct fcloop_lport *lport;
u64 nodename, portname;
- unsigned long flags;
int ret;
ret = fcloop_parse_nm_options(dev, &nodename, &portname, buf);
if (ret)
return ret;
- spin_lock_irqsave(&fcloop_lock, flags);
-
- 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;
- break;
- }
- }
- spin_unlock_irqrestore(&fcloop_lock, flags);
-
+ lport = fcloop_lport_lookup(nodename, portname);
if (!lport)
return -ENOENT;
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* Re: [PATCH v5 04/14] nvmet-fcloop: refactor fcloop_delete_local_port
2025-04-23 13:21 ` [PATCH v5 04/14] nvmet-fcloop: refactor fcloop_delete_local_port Daniel Wagner
@ 2025-04-24 10:10 ` Hannes Reinecke
2025-04-24 11:16 ` Daniel Wagner
0 siblings, 1 reply; 34+ messages in thread
From: Hannes Reinecke @ 2025-04-24 10:10 UTC (permalink / raw)
To: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni
Cc: Keith Busch, linux-nvme, linux-kernel
On 4/23/25 15:21, Daniel Wagner wrote:
> Use the newly introduced fcloop_lport_lookup instead
> of the open coded version.
>
> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> ---
> drivers/nvme/target/fcloop.c | 30 +++++++++++++++---------------
> 1 file changed, 15 insertions(+), 15 deletions(-)
>
You could merge it with the previous patch, but I guess that's
personal preference.
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v5 04/14] nvmet-fcloop: refactor fcloop_delete_local_port
2025-04-24 10:10 ` Hannes Reinecke
@ 2025-04-24 11:16 ` Daniel Wagner
0 siblings, 0 replies; 34+ messages in thread
From: Daniel Wagner @ 2025-04-24 11:16 UTC (permalink / raw)
To: Hannes Reinecke
Cc: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Keith Busch, linux-nvme, linux-kernel
On Thu, Apr 24, 2025 at 12:10:46PM +0200, Hannes Reinecke wrote:
> On 4/23/25 15:21, Daniel Wagner wrote:
> > Use the newly introduced fcloop_lport_lookup instead
> > of the open coded version.
> >
> > Signed-off-by: Daniel Wagner <wagi@kernel.org>
> > ---
> > drivers/nvme/target/fcloop.c | 30 +++++++++++++++---------------
> > 1 file changed, 15 insertions(+), 15 deletions(-)
> >
> You could merge it with the previous patch, but I guess that's
> personal preference.
I've decided to split this out, so the previous patch is simpler to
read but I merge it if this is better.
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v5 05/14] nvmet-fcloop: update refs on tfcp_req
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
` (3 preceding siblings ...)
2025-04-23 13:21 ` [PATCH v5 04/14] nvmet-fcloop: refactor fcloop_delete_local_port Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-04-23 13:21 ` [PATCH v5 06/14] nvmet-fcloop: add missing fcloop_callback_host_done Daniel Wagner
` (9 subsequent siblings)
14 siblings, 0 replies; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
Track the lifetime of the in-flight tfcp_req to ensure
the object is not freed too early.
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/target/fcloop.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index 1a8ae33c1699be3b7a2a7170dff77e324c127ebb..b54467b285181d6909c6592eb166cf4fe6fbe54c 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -566,7 +566,8 @@ fcloop_call_host_done(struct nvmefc_fcp_req *fcpreq,
}
/* release original io reference on tgt struct */
- fcloop_tfcp_req_put(tfcp_req);
+ if (tfcp_req)
+ fcloop_tfcp_req_put(tfcp_req);
}
static bool drop_fabric_opcode;
@@ -671,6 +672,7 @@ fcloop_fcp_abort_recv_work(struct work_struct *work)
break;
default:
spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
+ fcloop_tfcp_req_put(tfcp_req);
WARN_ON(1);
return;
}
@@ -958,8 +960,10 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport,
spin_lock(&inireq->inilock);
tfcp_req = inireq->tfcp_req;
- if (tfcp_req)
- fcloop_tfcp_req_get(tfcp_req);
+ if (tfcp_req) {
+ if (!fcloop_tfcp_req_get(tfcp_req))
+ tfcp_req = NULL;
+ }
spin_unlock(&inireq->inilock);
if (!tfcp_req)
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PATCH v5 06/14] nvmet-fcloop: add missing fcloop_callback_host_done
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
` (4 preceding siblings ...)
2025-04-23 13:21 ` [PATCH v5 05/14] nvmet-fcloop: update refs on tfcp_req Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-04-24 10:13 ` Hannes Reinecke
2025-04-23 13:21 ` [PATCH v5 07/14] nvmet-fcloop: access fcpreq only when holding reqlock Daniel Wagner
` (8 subsequent siblings)
14 siblings, 1 reply; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
Add the missing fcloop_call_host_done calls so that the caller
frees resources when something goes wrong.
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/target/fcloop.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index b54467b285181d6909c6592eb166cf4fe6fbe54c..0c0117e03adc81c643e90a7e7832ff087a4c2fd7 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -966,9 +966,10 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport,
}
spin_unlock(&inireq->inilock);
- if (!tfcp_req)
+ if (!tfcp_req) {
/* abort has already been called */
- return;
+ goto out_host_done;
+ }
/* break initiator/target relationship for io */
spin_lock_irqsave(&tfcp_req->reqlock, flags);
@@ -983,7 +984,7 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport,
default:
spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
WARN_ON(1);
- return;
+ goto out_host_done;
}
spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
@@ -997,6 +998,11 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport,
*/
fcloop_tfcp_req_put(tfcp_req);
}
+
+ return;
+
+out_host_done:
+ fcloop_call_host_done(fcpreq, tfcp_req, -ECANCELED);
}
static void
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* Re: [PATCH v5 06/14] nvmet-fcloop: add missing fcloop_callback_host_done
2025-04-23 13:21 ` [PATCH v5 06/14] nvmet-fcloop: add missing fcloop_callback_host_done Daniel Wagner
@ 2025-04-24 10:13 ` Hannes Reinecke
2025-04-24 11:26 ` Daniel Wagner
0 siblings, 1 reply; 34+ messages in thread
From: Hannes Reinecke @ 2025-04-24 10:13 UTC (permalink / raw)
To: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni
Cc: Keith Busch, linux-nvme, linux-kernel
On 4/23/25 15:21, Daniel Wagner wrote:
> Add the missing fcloop_call_host_done calls so that the caller
> frees resources when something goes wrong.
>
> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> ---
> drivers/nvme/target/fcloop.c | 12 +++++++++---
> 1 file changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
> index b54467b285181d6909c6592eb166cf4fe6fbe54c..0c0117e03adc81c643e90a7e7832ff087a4c2fd7 100644
> --- a/drivers/nvme/target/fcloop.c
> +++ b/drivers/nvme/target/fcloop.c
> @@ -966,9 +966,10 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport,
> }
> spin_unlock(&inireq->inilock);
>
> - if (!tfcp_req)
> + if (!tfcp_req) {
> /* abort has already been called */
> - return;
> + goto out_host_done;
> + }
>
Sure this is correct?
If the abort has been called I would have expected that all resources
are cleaned up by the abort, so we wouldn't need to do that here...
> /* break initiator/target relationship for io */
> spin_lock_irqsave(&tfcp_req->reqlock, flags);
> @@ -983,7 +984,7 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport,
> default:
> spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
> WARN_ON(1);
> - return;
> + goto out_host_done;
Do we still need the WARN_ON()? We can now gracefully recover, so a
simple log message would be sufficient here, no?
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PATCH v5 06/14] nvmet-fcloop: add missing fcloop_callback_host_done
2025-04-24 10:13 ` Hannes Reinecke
@ 2025-04-24 11:26 ` Daniel Wagner
0 siblings, 0 replies; 34+ messages in thread
From: Daniel Wagner @ 2025-04-24 11:26 UTC (permalink / raw)
To: Hannes Reinecke
Cc: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Keith Busch, linux-nvme, linux-kernel
On Thu, Apr 24, 2025 at 12:13:10PM +0200, Hannes Reinecke wrote:
> > + if (!tfcp_req) {
> > /* abort has already been called */
> > - return;
> > + goto out_host_done;
> > + }
> Sure this is correct?
> If the abort has been called I would have expected that all resources
> are cleaned up by the abort, so we wouldn't need to do that here...
Yes, it is still necessary to call fcpreq->done for this particular request.
> > @@ -983,7 +984,7 @@ fcloop_fcp_abort(struct nvme_fc_local_port *localport,
> > default:
> > spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
> > WARN_ON(1);
> > - return;
> > + goto out_host_done;
>
> Do we still need the WARN_ON()? We can now gracefully recover, so a
> simple log message would be sufficient here, no?
The WARN_ON is there because it catches when tfcp_req is in an wrong
state. It should be either in INI_IO_START, INI_IO_ACTIVE or
INI_IO_COMPLETED but never INI_IO_ABORT.
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v5 07/14] nvmet-fcloop: access fcpreq only when holding reqlock
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
` (5 preceding siblings ...)
2025-04-23 13:21 ` [PATCH v5 06/14] nvmet-fcloop: add missing fcloop_callback_host_done Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-04-24 10:15 ` Hannes Reinecke
2025-04-23 13:21 ` [PATCH v5 08/14] nvmet-fcloop: prevent double port deletion Daniel Wagner
` (7 subsequent siblings)
14 siblings, 1 reply; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
The abort handling logic expects that the state and the fcpreq are only
accessed when holding the reqlock lock.
While at it, only handle the aborts in the abort handler.
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/target/fcloop.c | 31 ++++++++++++++++---------------
1 file changed, 16 insertions(+), 15 deletions(-)
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index 0c0117e03adc81c643e90a7e7832ff087a4c2fd7..9adaee3c7129f7e270842c5d09f78de2e108479a 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -619,12 +619,13 @@ fcloop_fcp_recv_work(struct work_struct *work)
{
struct fcloop_fcpreq *tfcp_req =
container_of(work, struct fcloop_fcpreq, fcp_rcv_work);
- struct nvmefc_fcp_req *fcpreq = tfcp_req->fcpreq;
+ struct nvmefc_fcp_req *fcpreq;
unsigned long flags;
int ret = 0;
bool aborted = false;
spin_lock_irqsave(&tfcp_req->reqlock, flags);
+ fcpreq = tfcp_req->fcpreq;
switch (tfcp_req->inistate) {
case INI_IO_START:
tfcp_req->inistate = INI_IO_ACTIVE;
@@ -639,16 +640,19 @@ fcloop_fcp_recv_work(struct work_struct *work)
}
spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
- if (unlikely(aborted))
- ret = -ECANCELED;
- else {
- if (likely(!check_for_drop(tfcp_req)))
- ret = nvmet_fc_rcv_fcp_req(tfcp_req->tport->targetport,
- &tfcp_req->tgt_fcp_req,
- fcpreq->cmdaddr, fcpreq->cmdlen);
- else
- pr_info("%s: dropped command ********\n", __func__);
+ if (unlikely(aborted)) {
+ /* the abort handler will call fcloop_call_host_done */
+ return;
+ }
+
+ if (unlikely(check_for_drop(tfcp_req))) {
+ pr_info("%s: dropped command ********\n", __func__);
+ return;
}
+
+ ret = nvmet_fc_rcv_fcp_req(tfcp_req->tport->targetport,
+ &tfcp_req->tgt_fcp_req,
+ fcpreq->cmdaddr, fcpreq->cmdlen);
if (ret)
fcloop_call_host_done(fcpreq, tfcp_req, ret);
}
@@ -663,9 +667,10 @@ fcloop_fcp_abort_recv_work(struct work_struct *work)
unsigned long flags;
spin_lock_irqsave(&tfcp_req->reqlock, flags);
- fcpreq = tfcp_req->fcpreq;
switch (tfcp_req->inistate) {
case INI_IO_ABORTED:
+ fcpreq = tfcp_req->fcpreq;
+ tfcp_req->fcpreq = NULL;
break;
case INI_IO_COMPLETED:
completed = true;
@@ -688,10 +693,6 @@ fcloop_fcp_abort_recv_work(struct work_struct *work)
nvmet_fc_rcv_fcp_abort(tfcp_req->tport->targetport,
&tfcp_req->tgt_fcp_req);
- spin_lock_irqsave(&tfcp_req->reqlock, flags);
- tfcp_req->fcpreq = NULL;
- spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
-
fcloop_call_host_done(fcpreq, tfcp_req, -ECANCELED);
/* call_host_done releases reference for abort downcall */
}
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* Re: [PATCH v5 07/14] nvmet-fcloop: access fcpreq only when holding reqlock
2025-04-23 13:21 ` [PATCH v5 07/14] nvmet-fcloop: access fcpreq only when holding reqlock Daniel Wagner
@ 2025-04-24 10:15 ` Hannes Reinecke
2025-04-24 11:31 ` Daniel Wagner
0 siblings, 1 reply; 34+ messages in thread
From: Hannes Reinecke @ 2025-04-24 10:15 UTC (permalink / raw)
To: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni
Cc: Keith Busch, linux-nvme, linux-kernel
On 4/23/25 15:21, Daniel Wagner wrote:
> The abort handling logic expects that the state and the fcpreq are only
> accessed when holding the reqlock lock.
>
> While at it, only handle the aborts in the abort handler.
>
> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> ---
> drivers/nvme/target/fcloop.c | 31 ++++++++++++++++---------------
> 1 file changed, 16 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
> index 0c0117e03adc81c643e90a7e7832ff087a4c2fd7..9adaee3c7129f7e270842c5d09f78de2e108479a 100644
> --- a/drivers/nvme/target/fcloop.c
> +++ b/drivers/nvme/target/fcloop.c
> @@ -619,12 +619,13 @@ fcloop_fcp_recv_work(struct work_struct *work)
> {
> struct fcloop_fcpreq *tfcp_req =
> container_of(work, struct fcloop_fcpreq, fcp_rcv_work);
> - struct nvmefc_fcp_req *fcpreq = tfcp_req->fcpreq;
> + struct nvmefc_fcp_req *fcpreq;
> unsigned long flags;
> int ret = 0;
> bool aborted = false;
>
> spin_lock_irqsave(&tfcp_req->reqlock, flags);
> + fcpreq = tfcp_req->fcpreq;
> switch (tfcp_req->inistate) {
> case INI_IO_START:
> tfcp_req->inistate = INI_IO_ACTIVE;
> @@ -639,16 +640,19 @@ fcloop_fcp_recv_work(struct work_struct *work)
> }
> spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
>
> - if (unlikely(aborted))
> - ret = -ECANCELED;
> - else {
> - if (likely(!check_for_drop(tfcp_req)))
> - ret = nvmet_fc_rcv_fcp_req(tfcp_req->tport->targetport,
> - &tfcp_req->tgt_fcp_req,
> - fcpreq->cmdaddr, fcpreq->cmdlen);
> - else
> - pr_info("%s: dropped command ********\n", __func__);
> + if (unlikely(aborted)) {
> + /* the abort handler will call fcloop_call_host_done */
> + return;
> + }
> +
> + if (unlikely(check_for_drop(tfcp_req))) {
> + pr_info("%s: dropped command ********\n", __func__);
> + return;
> }
> +
> + ret = nvmet_fc_rcv_fcp_req(tfcp_req->tport->targetport,
> + &tfcp_req->tgt_fcp_req,
> + fcpreq->cmdaddr, fcpreq->cmdlen);
> if (ret)
> fcloop_call_host_done(fcpreq, tfcp_req, ret);
> }
> @@ -663,9 +667,10 @@ fcloop_fcp_abort_recv_work(struct work_struct *work)
> unsigned long flags;
>
> spin_lock_irqsave(&tfcp_req->reqlock, flags);
> - fcpreq = tfcp_req->fcpreq;
> switch (tfcp_req->inistate) {
> case INI_IO_ABORTED:
> + fcpreq = tfcp_req->fcpreq;
> + tfcp_req->fcpreq = NULL;
> break;
> case INI_IO_COMPLETED:
> completed = true;
> @@ -688,10 +693,6 @@ fcloop_fcp_abort_recv_work(struct work_struct *work)
> nvmet_fc_rcv_fcp_abort(tfcp_req->tport->targetport,
> &tfcp_req->tgt_fcp_req);
>
> - spin_lock_irqsave(&tfcp_req->reqlock, flags);
> - tfcp_req->fcpreq = NULL;
> - spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
> -
What happens for INI_IO_COMPLETED?
Don't we need to clear the 'fcpreq' pointer in that case, too?
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PATCH v5 07/14] nvmet-fcloop: access fcpreq only when holding reqlock
2025-04-24 10:15 ` Hannes Reinecke
@ 2025-04-24 11:31 ` Daniel Wagner
0 siblings, 0 replies; 34+ messages in thread
From: Daniel Wagner @ 2025-04-24 11:31 UTC (permalink / raw)
To: Hannes Reinecke
Cc: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Keith Busch, linux-nvme, linux-kernel
On Thu, Apr 24, 2025 at 12:15:20PM +0200, Hannes Reinecke wrote:
> > @@ -663,9 +667,10 @@ fcloop_fcp_abort_recv_work(struct work_struct *work)
> > unsigned long flags;
> > spin_lock_irqsave(&tfcp_req->reqlock, flags);
> > - fcpreq = tfcp_req->fcpreq;
> > switch (tfcp_req->inistate) {
> > case INI_IO_ABORTED:
> > + fcpreq = tfcp_req->fcpreq;
> > + tfcp_req->fcpreq = NULL;
> > break;
> > case INI_IO_COMPLETED:
> > completed = true;
> > @@ -688,10 +693,6 @@ fcloop_fcp_abort_recv_work(struct work_struct *work)
> > nvmet_fc_rcv_fcp_abort(tfcp_req->tport->targetport,
> > &tfcp_req->tgt_fcp_req);
> > - spin_lock_irqsave(&tfcp_req->reqlock, flags);
> > - tfcp_req->fcpreq = NULL;
> > - spin_unlock_irqrestore(&tfcp_req->reqlock, flags);
> > -
> What happens for INI_IO_COMPLETED?
The request was completed before the abort handler was running. Thus
nothing to do here.
> Don't we need to clear the 'fcpreq' pointer in that case, too?
The normal code path has already taken care of this request and the only
thing left to do is to give back the refcount on the tfcp_req to free
the memory.
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v5 08/14] nvmet-fcloop: prevent double port deletion
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
` (6 preceding siblings ...)
2025-04-23 13:21 ` [PATCH v5 07/14] nvmet-fcloop: access fcpreq only when holding reqlock Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-04-24 10:17 ` Hannes Reinecke
2025-04-23 13:21 ` [PATCH v5 09/14] nvmet-fcloop: allocate/free fcloop_lsreq directly Daniel Wagner
` (6 subsequent siblings)
14 siblings, 1 reply; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
The delete callback can be called either via the unregister function or
from the transport directly. Thus it is necessary ensure resources are
not freed multiple times.
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/target/fcloop.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index 9adaee3c7129f7e270842c5d09f78de2e108479a..014cc66f92cb1db0a81a79d2109eae3fff5fd38a 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -215,6 +215,9 @@ struct fcloop_lport_priv {
struct fcloop_lport *lport;
};
+/* The port is already being removed, avoid double free */
+#define PORT_DELETED 0
+
struct fcloop_rport {
struct nvme_fc_remote_port *remoteport;
struct nvmet_fc_target_port *targetport;
@@ -223,6 +226,7 @@ struct fcloop_rport {
spinlock_t lock;
struct list_head ls_list;
struct work_struct ls_work;
+ unsigned long flags;
};
struct fcloop_tport {
@@ -233,6 +237,7 @@ struct fcloop_tport {
spinlock_t lock;
struct list_head ls_list;
struct work_struct ls_work;
+ unsigned long flags;
};
struct fcloop_nport {
@@ -1067,14 +1072,20 @@ static void
fcloop_remoteport_delete(struct nvme_fc_remote_port *remoteport)
{
struct fcloop_rport *rport = remoteport->private;
+ bool delete_port = true;
unsigned long flags;
flush_work(&rport->ls_work);
spin_lock_irqsave(&fcloop_lock, flags);
+ if (test_and_set_bit(PORT_DELETED, &rport->flags))
+ delete_port = false;
rport->nport->rport = NULL;
spin_unlock_irqrestore(&fcloop_lock, flags);
+ if (!delete_port)
+ return;
+
fcloop_nport_put(rport->nport);
}
@@ -1082,14 +1093,20 @@ static void
fcloop_targetport_delete(struct nvmet_fc_target_port *targetport)
{
struct fcloop_tport *tport = targetport->private;
+ bool delete_port = true;
unsigned long flags;
flush_work(&tport->ls_work);
spin_lock_irqsave(&fcloop_lock, flags);
+ if (test_and_set_bit(PORT_DELETED, &tport->flags))
+ delete_port = false;
tport->nport->tport = NULL;
spin_unlock_irqrestore(&fcloop_lock, flags);
+ if (!delete_port)
+ return;
+
fcloop_nport_put(tport->nport);
}
@@ -1433,6 +1450,7 @@ fcloop_create_remote_port(struct device *dev, struct device_attribute *attr,
rport->nport = nport;
rport->lport = nport->lport;
nport->rport = rport;
+ rport->flags = 0;
spin_lock_init(&rport->lock);
INIT_WORK(&rport->ls_work, fcloop_rport_lsrqst_work);
INIT_LIST_HEAD(&rport->ls_list);
@@ -1530,6 +1548,7 @@ fcloop_create_target_port(struct device *dev, struct device_attribute *attr,
tport->nport = nport;
tport->lport = nport->lport;
nport->tport = tport;
+ tport->flags = 0;
spin_lock_init(&tport->lock);
INIT_WORK(&tport->ls_work, fcloop_tport_lsrqst_work);
INIT_LIST_HEAD(&tport->ls_list);
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* Re: [PATCH v5 08/14] nvmet-fcloop: prevent double port deletion
2025-04-23 13:21 ` [PATCH v5 08/14] nvmet-fcloop: prevent double port deletion Daniel Wagner
@ 2025-04-24 10:17 ` Hannes Reinecke
2025-04-24 11:32 ` Daniel Wagner
0 siblings, 1 reply; 34+ messages in thread
From: Hannes Reinecke @ 2025-04-24 10:17 UTC (permalink / raw)
To: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni
Cc: Keith Busch, linux-nvme, linux-kernel
On 4/23/25 15:21, Daniel Wagner wrote:
> The delete callback can be called either via the unregister function or
> from the transport directly. Thus it is necessary ensure resources are
> not freed multiple times.
>
> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> ---
> drivers/nvme/target/fcloop.c | 19 +++++++++++++++++++
> 1 file changed, 19 insertions(+)
>
> diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
> index 9adaee3c7129f7e270842c5d09f78de2e108479a..014cc66f92cb1db0a81a79d2109eae3fff5fd38a 100644
> --- a/drivers/nvme/target/fcloop.c
> +++ b/drivers/nvme/target/fcloop.c
> @@ -215,6 +215,9 @@ struct fcloop_lport_priv {
> struct fcloop_lport *lport;
> };
>
> +/* The port is already being removed, avoid double free */
> +#define PORT_DELETED 0
> +
> struct fcloop_rport {
> struct nvme_fc_remote_port *remoteport;
> struct nvmet_fc_target_port *targetport;
> @@ -223,6 +226,7 @@ struct fcloop_rport {
> spinlock_t lock;
> struct list_head ls_list;
> struct work_struct ls_work;
> + unsigned long flags;
> };
>
> struct fcloop_tport {
> @@ -233,6 +237,7 @@ struct fcloop_tport {
> spinlock_t lock;
> struct list_head ls_list;
> struct work_struct ls_work;
> + unsigned long flags;
> };
>
> struct fcloop_nport {
> @@ -1067,14 +1072,20 @@ static void
> fcloop_remoteport_delete(struct nvme_fc_remote_port *remoteport)
> {
> struct fcloop_rport *rport = remoteport->private;
> + bool delete_port = true;
> unsigned long flags;
>
> flush_work(&rport->ls_work);
>
> spin_lock_irqsave(&fcloop_lock, flags);
> + if (test_and_set_bit(PORT_DELETED, &rport->flags))
> + delete_port = false;
> rport->nport->rport = NULL;
> spin_unlock_irqrestore(&fcloop_lock, flags);
>
> + if (!delete_port)
> + return;
> +
The double negation is hard to follow. Can't you
rename it to 'put_port' or somesuch and invert the logic?
> fcloop_nport_put(rport->nport);
> }
>
> @@ -1082,14 +1093,20 @@ static void
> fcloop_targetport_delete(struct nvmet_fc_target_port *targetport)
> {
> struct fcloop_tport *tport = targetport->private;
> + bool delete_port = true;
> unsigned long flags;
>
> flush_work(&tport->ls_work);
>
> spin_lock_irqsave(&fcloop_lock, flags);
> + if (test_and_set_bit(PORT_DELETED, &tport->flags))
> + delete_port = false;
> tport->nport->tport = NULL;
> spin_unlock_irqrestore(&fcloop_lock, flags);
>
> + if (!delete_port)
> + return;
> +
Same here.
> fcloop_nport_put(tport->nport);
> }
>
> @@ -1433,6 +1450,7 @@ fcloop_create_remote_port(struct device *dev, struct device_attribute *attr,
> rport->nport = nport;
> rport->lport = nport->lport;
> nport->rport = rport;
> + rport->flags = 0;
> spin_lock_init(&rport->lock);
> INIT_WORK(&rport->ls_work, fcloop_rport_lsrqst_work);
> INIT_LIST_HEAD(&rport->ls_list);
> @@ -1530,6 +1548,7 @@ fcloop_create_target_port(struct device *dev, struct device_attribute *attr,
> tport->nport = nport;
> tport->lport = nport->lport;
> nport->tport = tport;
> + tport->flags = 0;
> spin_lock_init(&tport->lock);
> INIT_WORK(&tport->ls_work, fcloop_tport_lsrqst_work);
> INIT_LIST_HEAD(&tport->ls_list);
>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PATCH v5 08/14] nvmet-fcloop: prevent double port deletion
2025-04-24 10:17 ` Hannes Reinecke
@ 2025-04-24 11:32 ` Daniel Wagner
0 siblings, 0 replies; 34+ messages in thread
From: Daniel Wagner @ 2025-04-24 11:32 UTC (permalink / raw)
To: Hannes Reinecke
Cc: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Keith Busch, linux-nvme, linux-kernel
On Thu, Apr 24, 2025 at 12:17:32PM +0200, Hannes Reinecke wrote:
> > struct fcloop_rport *rport = remoteport->private;
> > + bool delete_port = true;
> > unsigned long flags;
> > flush_work(&rport->ls_work);
> > spin_lock_irqsave(&fcloop_lock, flags);
> > + if (test_and_set_bit(PORT_DELETED, &rport->flags))
> > + delete_port = false;
> > rport->nport->rport = NULL;
> > spin_unlock_irqrestore(&fcloop_lock, flags);
> > + if (!delete_port)
> > + return;
> > +
>
> The double negation is hard to follow. Can't you
> rename it to 'put_port' or somesuch and invert the logic?
Yes, was also not really happy with it but didn't really come up with a
good name. Thanks!
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v5 09/14] nvmet-fcloop: allocate/free fcloop_lsreq directly
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
` (7 preceding siblings ...)
2025-04-23 13:21 ` [PATCH v5 08/14] nvmet-fcloop: prevent double port deletion Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-04-24 10:18 ` Hannes Reinecke
2025-05-07 10:48 ` kernel test robot
2025-04-23 13:21 ` [PATCH v5 10/14] nvmet-fcloop: don't wait for lport cleanup Daniel Wagner
` (5 subsequent siblings)
14 siblings, 2 replies; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
fcloop depends on the host or the target to allocate the fcloop_lsreq
object. This means that the lifetime of the fcloop_lsreq is tied to
either the host or the target. Consequently, the host or the target must
cooperate during shutdown.
Unfortunately, this approach does not work well when the target forces a
shutdown, as there are dependencies that are difficult to resolve in a
clean way.
The simplest solution is to decouple the lifetime of the fcloop_lsreq
object by managing them directly within fcloop. Since this is not a
performance-critical path and only a small number of LS objects are used
during setup and cleanup, it does not significantly impact performance
to allocate them during normal operation.
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/target/fcloop.c | 63 +++++++++++++++++++++++++++++++-------------
1 file changed, 45 insertions(+), 18 deletions(-)
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index 014cc66f92cb1db0a81a79d2109eae3fff5fd38a..47ce51183a66b4b37fc850cced2ddf70be2cdb42 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -293,6 +293,9 @@ struct fcloop_ini_fcpreq {
spinlock_t inilock;
};
+/* SLAB cache for fcloop_lsreq structures */
+struct kmem_cache *lsreq_cache;
+
static inline struct fcloop_lsreq *
ls_rsp_to_lsreq(struct nvmefc_ls_rsp *lsrsp)
{
@@ -343,6 +346,7 @@ fcloop_rport_lsrqst_work(struct work_struct *work)
* callee may free memory containing tls_req.
* do not reference lsreq after this.
*/
+ kmem_cache_free(lsreq_cache, tls_req);
spin_lock(&rport->lock);
}
@@ -354,10 +358,13 @@ fcloop_h2t_ls_req(struct nvme_fc_local_port *localport,
struct nvme_fc_remote_port *remoteport,
struct nvmefc_ls_req *lsreq)
{
- struct fcloop_lsreq *tls_req = lsreq->private;
struct fcloop_rport *rport = remoteport->private;
+ struct fcloop_lsreq *tls_req;
int ret = 0;
+ tls_req = kmem_cache_alloc(lsreq_cache, GFP_KERNEL);
+ if (!tls_req)
+ return -ENOMEM;
tls_req->lsreq = lsreq;
INIT_LIST_HEAD(&tls_req->ls_list);
@@ -394,14 +401,17 @@ fcloop_h2t_xmt_ls_rsp(struct nvmet_fc_target_port *targetport,
lsrsp->done(lsrsp);
- if (remoteport) {
- rport = remoteport->private;
- spin_lock(&rport->lock);
- list_add_tail(&tls_req->ls_list, &rport->ls_list);
- spin_unlock(&rport->lock);
- queue_work(nvmet_wq, &rport->ls_work);
+ if (!remoteport) {
+ kmem_cache_free(lsreq_cache, tls_req);
+ return 0;
}
+ rport = remoteport->private;
+ spin_lock(&rport->lock);
+ list_add_tail(&tls_req->ls_list, &rport->ls_list);
+ spin_unlock(&rport->lock);
+ queue_work(nvmet_wq, &rport->ls_work);
+
return 0;
}
@@ -427,6 +437,7 @@ fcloop_tport_lsrqst_work(struct work_struct *work)
* callee may free memory containing tls_req.
* do not reference lsreq after this.
*/
+ kmem_cache_free(lsreq_cache, tls_req);
spin_lock(&tport->lock);
}
@@ -437,8 +448,8 @@ static int
fcloop_t2h_ls_req(struct nvmet_fc_target_port *targetport, void *hosthandle,
struct nvmefc_ls_req *lsreq)
{
- struct fcloop_lsreq *tls_req = lsreq->private;
struct fcloop_tport *tport = targetport->private;
+ struct fcloop_lsreq *tls_req;
int ret = 0;
/*
@@ -446,6 +457,10 @@ fcloop_t2h_ls_req(struct nvmet_fc_target_port *targetport, void *hosthandle,
* hosthandle ignored as fcloop currently is
* 1:1 tgtport vs remoteport
*/
+
+ tls_req = kmem_cache_alloc(lsreq_cache, GFP_KERNEL);
+ if (!tls_req)
+ return -ENOMEM;
tls_req->lsreq = lsreq;
INIT_LIST_HEAD(&tls_req->ls_list);
@@ -462,6 +477,9 @@ fcloop_t2h_ls_req(struct nvmet_fc_target_port *targetport, void *hosthandle,
ret = nvme_fc_rcv_ls_req(tport->remoteport, &tls_req->ls_rsp,
lsreq->rqstaddr, lsreq->rqstlen);
+ if (ret)
+ kmem_cache_free(lsreq_cache, tls_req);
+
return ret;
}
@@ -481,14 +499,17 @@ fcloop_t2h_xmt_ls_rsp(struct nvme_fc_local_port *localport,
lsreq->rsplen : lsrsp->rsplen));
lsrsp->done(lsrsp);
- if (targetport) {
- tport = targetport->private;
- spin_lock(&tport->lock);
- list_add_tail(&tls_req->ls_list, &tport->ls_list);
- spin_unlock(&tport->lock);
- queue_work(nvmet_wq, &tport->ls_work);
+ if (!targetport) {
+ kmem_cache_free(lsreq_cache, tls_req);
+ return 0;
}
+ tport = targetport->private;
+ spin_lock(&tport->lock);
+ list_add_tail(&tls_req->ls_list, &tport->ls_list);
+ spin_unlock(&tport->lock);
+ queue_work(nvmet_wq, &tport->ls_work);
+
return 0;
}
@@ -1131,7 +1152,6 @@ static struct nvme_fc_port_template fctemplate = {
/* sizes of additional private data for data structures */
.local_priv_sz = sizeof(struct fcloop_lport_priv),
.remote_priv_sz = sizeof(struct fcloop_rport),
- .lsrqst_priv_sz = sizeof(struct fcloop_lsreq),
.fcprqst_priv_sz = sizeof(struct fcloop_ini_fcpreq),
};
@@ -1154,7 +1174,6 @@ static struct nvmet_fc_target_template tgttemplate = {
.target_features = 0,
/* sizes of additional private data for data structures */
.target_priv_sz = sizeof(struct fcloop_tport),
- .lsrqst_priv_sz = sizeof(struct fcloop_lsreq),
};
static ssize_t
@@ -1674,15 +1693,20 @@ static const struct class fcloop_class = {
};
static struct device *fcloop_device;
-
static int __init fcloop_init(void)
{
int ret;
+ lsreq_cache = kmem_cache_create("lsreq_cache",
+ sizeof(struct fcloop_lsreq), 0,
+ 0, NULL);
+ if (!lsreq_cache)
+ return -ENOMEM;
+
ret = class_register(&fcloop_class);
if (ret) {
pr_err("couldn't register class fcloop\n");
- return ret;
+ goto out_destroy_cache;
}
fcloop_device = device_create_with_groups(
@@ -1700,6 +1724,8 @@ static int __init fcloop_init(void)
out_destroy_class:
class_unregister(&fcloop_class);
+out_destroy_cache:
+ kmem_cache_destroy(lsreq_cache);
return ret;
}
@@ -1765,6 +1791,7 @@ static void __exit fcloop_exit(void)
device_destroy(&fcloop_class, MKDEV(0, 0));
class_unregister(&fcloop_class);
+ kmem_cache_destroy(lsreq_cache);
}
module_init(fcloop_init);
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* Re: [PATCH v5 09/14] nvmet-fcloop: allocate/free fcloop_lsreq directly
2025-04-23 13:21 ` [PATCH v5 09/14] nvmet-fcloop: allocate/free fcloop_lsreq directly Daniel Wagner
@ 2025-04-24 10:18 ` Hannes Reinecke
2025-05-07 10:48 ` kernel test robot
1 sibling, 0 replies; 34+ messages in thread
From: Hannes Reinecke @ 2025-04-24 10:18 UTC (permalink / raw)
To: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni
Cc: Keith Busch, linux-nvme, linux-kernel
On 4/23/25 15:21, Daniel Wagner wrote:
> fcloop depends on the host or the target to allocate the fcloop_lsreq
> object. This means that the lifetime of the fcloop_lsreq is tied to
> either the host or the target. Consequently, the host or the target must
> cooperate during shutdown.
>
> Unfortunately, this approach does not work well when the target forces a
> shutdown, as there are dependencies that are difficult to resolve in a
> clean way.
>
> The simplest solution is to decouple the lifetime of the fcloop_lsreq
> object by managing them directly within fcloop. Since this is not a
> performance-critical path and only a small number of LS objects are used
> during setup and cleanup, it does not significantly impact performance
> to allocate them during normal operation.
>
> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> ---
> drivers/nvme/target/fcloop.c | 63 +++++++++++++++++++++++++++++++-------------
> 1 file changed, 45 insertions(+), 18 deletions(-)
>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 34+ messages in thread
* Re: [PATCH v5 09/14] nvmet-fcloop: allocate/free fcloop_lsreq directly
2025-04-23 13:21 ` [PATCH v5 09/14] nvmet-fcloop: allocate/free fcloop_lsreq directly Daniel Wagner
2025-04-24 10:18 ` Hannes Reinecke
@ 2025-05-07 10:48 ` kernel test robot
1 sibling, 0 replies; 34+ messages in thread
From: kernel test robot @ 2025-05-07 10:48 UTC (permalink / raw)
To: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni
Cc: oe-kbuild-all, Hannes Reinecke, Keith Busch, linux-nvme,
linux-kernel, Daniel Wagner
Hi Daniel,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 3d7aa0c7b4e96cd460826d932e44710cdeb3378b]
url: https://github.com/intel-lab-lkp/linux/commits/Daniel-Wagner/nvmet-fcloop-track-ref-counts-for-nports/20250423-212643
base: 3d7aa0c7b4e96cd460826d932e44710cdeb3378b
patch link: https://lore.kernel.org/r/20250423-nvmet-fcloop-v5-9-3d7f968728a5%40kernel.org
patch subject: [PATCH v5 09/14] nvmet-fcloop: allocate/free fcloop_lsreq directly
config: x86_64-randconfig-r131-20250426 (https://download.01.org/0day-ci/archive/20250507/202505071848.zYLh3Kvb-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250507/202505071848.zYLh3Kvb-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202505071848.zYLh3Kvb-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
>> drivers/nvme/target/fcloop.c:297:19: sparse: sparse: symbol 'lsreq_cache' was not declared. Should it be static?
vim +/lsreq_cache +297 drivers/nvme/target/fcloop.c
295
296 /* SLAB cache for fcloop_lsreq structures */
> 297 struct kmem_cache *lsreq_cache;
298
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v5 10/14] nvmet-fcloop: don't wait for lport cleanup
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
` (8 preceding siblings ...)
2025-04-23 13:21 ` [PATCH v5 09/14] nvmet-fcloop: allocate/free fcloop_lsreq directly Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-04-24 10:21 ` Hannes Reinecke
2025-04-23 13:21 ` [PATCH v5 11/14] nvmet-fcloop: drop response if targetport is gone Daniel Wagner
` (4 subsequent siblings)
14 siblings, 1 reply; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
The lifetime of the fcloop_lsreq is not tight to the lifetime of the
host or target port, thus there is no need anymore to synchronize the
cleanup path anymore.
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/target/fcloop.c | 21 ++++-----------------
1 file changed, 4 insertions(+), 17 deletions(-)
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index 47ce51183a66b4b37fc850cced2ddf70be2cdb42..ca11230bffedb0f2313a99e24e54e892daf0d644 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -207,7 +207,6 @@ static LIST_HEAD(fcloop_nports);
struct fcloop_lport {
struct nvme_fc_local_port *localport;
struct list_head lport_list;
- struct completion unreg_done;
refcount_t ref;
};
@@ -1083,9 +1082,6 @@ fcloop_localport_delete(struct nvme_fc_local_port *localport)
struct fcloop_lport_priv *lport_priv = localport->private;
struct fcloop_lport *lport = lport_priv->lport;
- /* release any threads waiting for the unreg to complete */
- complete(&lport->unreg_done);
-
fcloop_lport_put(lport);
}
@@ -1238,18 +1234,9 @@ fcloop_create_local_port(struct device *dev, struct device_attribute *attr,
}
static int
-__wait_localport_unreg(struct fcloop_lport *lport)
+__localport_unreg(struct fcloop_lport *lport)
{
- int ret;
-
- init_completion(&lport->unreg_done);
-
- ret = nvme_fc_unregister_localport(lport->localport);
-
- if (!ret)
- wait_for_completion(&lport->unreg_done);
-
- return ret;
+ return nvme_fc_unregister_localport(lport->localport);
}
static struct fcloop_nport *
@@ -1332,7 +1319,7 @@ fcloop_delete_local_port(struct device *dev, struct device_attribute *attr,
if (!lport)
return -ENOENT;
- ret = __wait_localport_unreg(lport);
+ ret = __localport_unreg(lport);
fcloop_lport_put(lport);
return ret ? ret : count;
@@ -1776,7 +1763,7 @@ static void __exit fcloop_exit(void)
spin_unlock_irqrestore(&fcloop_lock, flags);
- ret = __wait_localport_unreg(lport);
+ ret = __localport_unreg(lport);
if (ret)
pr_warn("%s: Failed deleting local port\n", __func__);
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* Re: [PATCH v5 10/14] nvmet-fcloop: don't wait for lport cleanup
2025-04-23 13:21 ` [PATCH v5 10/14] nvmet-fcloop: don't wait for lport cleanup Daniel Wagner
@ 2025-04-24 10:21 ` Hannes Reinecke
2025-04-24 11:48 ` Daniel Wagner
0 siblings, 1 reply; 34+ messages in thread
From: Hannes Reinecke @ 2025-04-24 10:21 UTC (permalink / raw)
To: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni
Cc: Keith Busch, linux-nvme, linux-kernel
On 4/23/25 15:21, Daniel Wagner wrote:
> The lifetime of the fcloop_lsreq is not tight to the lifetime of the
> host or target port, thus there is no need anymore to synchronize the
> cleanup path anymore.
>
> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> ---
> drivers/nvme/target/fcloop.c | 21 ++++-----------------
> 1 file changed, 4 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
> index 47ce51183a66b4b37fc850cced2ddf70be2cdb42..ca11230bffedb0f2313a99e24e54e892daf0d644 100644
> --- a/drivers/nvme/target/fcloop.c
> +++ b/drivers/nvme/target/fcloop.c
> @@ -207,7 +207,6 @@ static LIST_HEAD(fcloop_nports);
> struct fcloop_lport {
> struct nvme_fc_local_port *localport;
> struct list_head lport_list;
> - struct completion unreg_done;
> refcount_t ref;
> };
>
> @@ -1083,9 +1082,6 @@ fcloop_localport_delete(struct nvme_fc_local_port *localport)
> struct fcloop_lport_priv *lport_priv = localport->private;
> struct fcloop_lport *lport = lport_priv->lport;
>
> - /* release any threads waiting for the unreg to complete */
> - complete(&lport->unreg_done);
> -
> fcloop_lport_put(lport);
> }
>
> @@ -1238,18 +1234,9 @@ fcloop_create_local_port(struct device *dev, struct device_attribute *attr,
> }
>
> static int
> -__wait_localport_unreg(struct fcloop_lport *lport)
> +__localport_unreg(struct fcloop_lport *lport)
> {
> - int ret;
> -
> - init_completion(&lport->unreg_done);
> -
> - ret = nvme_fc_unregister_localport(lport->localport);
> -
> - if (!ret)
> - wait_for_completion(&lport->unreg_done);
> -
> - return ret;
> + return nvme_fc_unregister_localport(lport->localport);
> }
>
> static struct fcloop_nport *
> @@ -1332,7 +1319,7 @@ fcloop_delete_local_port(struct device *dev, struct device_attribute *attr,
> if (!lport)
> return -ENOENT;
>
> - ret = __wait_localport_unreg(lport);
> + ret = __localport_unreg(lport);
> fcloop_lport_put(lport);
>
> return ret ? ret : count;
> @@ -1776,7 +1763,7 @@ static void __exit fcloop_exit(void)
>
> spin_unlock_irqrestore(&fcloop_lock, flags);
>
> - ret = __wait_localport_unreg(lport);
> + ret = __localport_unreg(lport);
> if (ret)
> pr_warn("%s: Failed deleting local port\n", __func__);
>
>
And who is freeing the allocated fcloop_lsreq structures?
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PATCH v5 10/14] nvmet-fcloop: don't wait for lport cleanup
2025-04-24 10:21 ` Hannes Reinecke
@ 2025-04-24 11:48 ` Daniel Wagner
2025-04-24 12:10 ` Hannes Reinecke
0 siblings, 1 reply; 34+ messages in thread
From: Daniel Wagner @ 2025-04-24 11:48 UTC (permalink / raw)
To: Hannes Reinecke
Cc: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Keith Busch, linux-nvme, linux-kernel
On Thu, Apr 24, 2025 at 12:21:09PM +0200, Hannes Reinecke wrote:
> > @@ -1776,7 +1763,7 @@ static void __exit fcloop_exit(void)
> > spin_unlock_irqrestore(&fcloop_lock, flags);
> > - ret = __wait_localport_unreg(lport);
> > + ret = __localport_unreg(lport);
> > if (ret)
> > pr_warn("%s: Failed deleting local port\n", __func__);
> >
> And who is freeing the allocated fcloop_lsreq structures?
After a fcloop_lsreq is allocated it is put on either rport->ls_list or
tport->ls_list and later freed in fcloop_tport_lsrqst_work, resp.
fcloop_rport_lsrqst_work for the non-error case (or in the callbacks).
That means when the localport is unregistered successfully there are no
fcloop_lsreq around. The issue is what to do when __localport_unreq
returns an error.
This code could loop forever when the port_state is not in
FC_OBJSTATE_ONLINE when calling nvme_fc_unregister_localport(). This
should not happen (it did before this series) but now it shouldn't be
the case anymore. I suppose a pr_warn_once() and a sleep might be a good
idea to avoid a busy loop?
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PATCH v5 10/14] nvmet-fcloop: don't wait for lport cleanup
2025-04-24 11:48 ` Daniel Wagner
@ 2025-04-24 12:10 ` Hannes Reinecke
2025-04-24 12:58 ` Daniel Wagner
0 siblings, 1 reply; 34+ messages in thread
From: Hannes Reinecke @ 2025-04-24 12:10 UTC (permalink / raw)
To: Daniel Wagner
Cc: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Keith Busch, linux-nvme, linux-kernel
On 4/24/25 13:48, Daniel Wagner wrote:
> On Thu, Apr 24, 2025 at 12:21:09PM +0200, Hannes Reinecke wrote:
>>> @@ -1776,7 +1763,7 @@ static void __exit fcloop_exit(void)
>>> spin_unlock_irqrestore(&fcloop_lock, flags);
>>> - ret = __wait_localport_unreg(lport);
>>> + ret = __localport_unreg(lport);
>>> if (ret)
>>> pr_warn("%s: Failed deleting local port\n", __func__);
>>>
>> And who is freeing the allocated fcloop_lsreq structures?
>
> After a fcloop_lsreq is allocated it is put on either rport->ls_list or
> tport->ls_list and later freed in fcloop_tport_lsrqst_work, resp.
> fcloop_rport_lsrqst_work for the non-error case (or in the callbacks).
>
> That means when the localport is unregistered successfully there are no
> fcloop_lsreq around. The issue is what to do when __localport_unreq
> returns an error.
>
> This code could loop forever when the port_state is not in
> FC_OBJSTATE_ONLINE when calling nvme_fc_unregister_localport(). This
> should not happen (it did before this series) but now it shouldn't be
> the case anymore. I suppose a pr_warn_once() and a sleep might be a good
> idea to avoid a busy loop?
My point was more: you call kmem_cache_destroy() unconditionally upon
exit. But the boilerplate says that you have to free all allocations
from the kmem_cache before that call.
Yet the exit function doesn't do that.
Question is: what are the guarantees that the cache is empty upon exit?
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 34+ messages in thread* Re: [PATCH v5 10/14] nvmet-fcloop: don't wait for lport cleanup
2025-04-24 12:10 ` Hannes Reinecke
@ 2025-04-24 12:58 ` Daniel Wagner
0 siblings, 0 replies; 34+ messages in thread
From: Daniel Wagner @ 2025-04-24 12:58 UTC (permalink / raw)
To: Hannes Reinecke
Cc: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Keith Busch, linux-nvme, linux-kernel
On Thu, Apr 24, 2025 at 02:10:20PM +0200, Hannes Reinecke wrote:
> My point was more: you call kmem_cache_destroy() unconditionally upon
> exit. But the boilerplate says that you have to free all allocations
> from the kmem_cache before that call.
> Yet the exit function doesn't do that.
> Question is: what are the guarantees that the cache is empty upon exit?
The first loop will only terminate when all request have been freed,
thus it is safe to destroy the cache afterwards.
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v5 11/14] nvmet-fcloop: drop response if targetport is gone
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
` (9 preceding siblings ...)
2025-04-23 13:21 ` [PATCH v5 10/14] nvmet-fcloop: don't wait for lport cleanup Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-04-24 10:21 ` Hannes Reinecke
2025-04-23 13:21 ` [PATCH v5 12/14] nvmet-fc: free pending reqs on tgtport unregister Daniel Wagner
` (3 subsequent siblings)
14 siblings, 1 reply; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
When the target port is gone, the lsrsp pointer is invalid. Thus don't
call the done function anymore instead just drop the response.
This happens when the target sends a disconnect association. After this
the target starts tearing down all resources and doesn't expect any
response.
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/target/fcloop.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/drivers/nvme/target/fcloop.c b/drivers/nvme/target/fcloop.c
index ca11230bffedb0f2313a99e24e54e892daf0d644..bb45721ec0f028399d810cbd7891bc6975801752 100644
--- a/drivers/nvme/target/fcloop.c
+++ b/drivers/nvme/target/fcloop.c
@@ -493,16 +493,25 @@ fcloop_t2h_xmt_ls_rsp(struct nvme_fc_local_port *localport,
struct nvmet_fc_target_port *targetport = rport->targetport;
struct fcloop_tport *tport;
- memcpy(lsreq->rspaddr, lsrsp->rspbuf,
- ((lsreq->rsplen < lsrsp->rsplen) ?
- lsreq->rsplen : lsrsp->rsplen));
- lsrsp->done(lsrsp);
-
if (!targetport) {
+ /*
+ * The target port is gone. The target doesn't expect any
+ * response anymore and the ->done call is not valid
+ * because the resources have been freed by
+ * nvmet_fc_free_pending_reqs.
+ *
+ * We end up here from delete association exchange:
+ * nvmet_fc_xmt_disconnect_assoc sends an async request.
+ */
kmem_cache_free(lsreq_cache, tls_req);
return 0;
}
+ memcpy(lsreq->rspaddr, lsrsp->rspbuf,
+ ((lsreq->rsplen < lsrsp->rsplen) ?
+ lsreq->rsplen : lsrsp->rsplen));
+ lsrsp->done(lsrsp);
+
tport = targetport->private;
spin_lock(&tport->lock);
list_add_tail(&tls_req->ls_list, &tport->ls_list);
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* Re: [PATCH v5 11/14] nvmet-fcloop: drop response if targetport is gone
2025-04-23 13:21 ` [PATCH v5 11/14] nvmet-fcloop: drop response if targetport is gone Daniel Wagner
@ 2025-04-24 10:21 ` Hannes Reinecke
0 siblings, 0 replies; 34+ messages in thread
From: Hannes Reinecke @ 2025-04-24 10:21 UTC (permalink / raw)
To: Daniel Wagner, James Smart, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni
Cc: Keith Busch, linux-nvme, linux-kernel
On 4/23/25 15:21, Daniel Wagner wrote:
> When the target port is gone, the lsrsp pointer is invalid. Thus don't
> call the done function anymore instead just drop the response.
>
> This happens when the target sends a disconnect association. After this
> the target starts tearing down all resources and doesn't expect any
> response.
>
> Signed-off-by: Daniel Wagner <wagi@kernel.org>
> ---
> drivers/nvme/target/fcloop.c | 19 ++++++++++++++-----
> 1 file changed, 14 insertions(+), 5 deletions(-)
>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
^ permalink raw reply [flat|nested] 34+ messages in thread
* [PATCH v5 12/14] nvmet-fc: free pending reqs on tgtport unregister
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
` (10 preceding siblings ...)
2025-04-23 13:21 ` [PATCH v5 11/14] nvmet-fcloop: drop response if targetport is gone Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-04-23 13:21 ` [PATCH v5 13/14] nvmet-fc: take tgtport refs for portentry Daniel Wagner
` (2 subsequent siblings)
14 siblings, 0 replies; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
When nvmet_fc_unregister_targetport is called by the LLDD, it's not
possible to communicate with the host, thus all pending request will not
be process. Thus explicitly free them.
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/target/fc.c | 41 ++++++++++++++++++++++++++++++++++-------
1 file changed, 34 insertions(+), 7 deletions(-)
diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c
index 7b50130f10f6578e6e49fe8ea661de34dfbb3683..75ddb7425605dd6623db38a133b63e201592354c 100644
--- a/drivers/nvme/target/fc.c
+++ b/drivers/nvme/target/fc.c
@@ -1580,6 +1580,39 @@ nvmet_fc_delete_ctrl(struct nvmet_ctrl *ctrl)
spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags);
}
+static void
+nvmet_fc_free_pending_reqs(struct nvmet_fc_tgtport *tgtport)
+{
+ struct nvmet_fc_ls_req_op *lsop;
+ struct nvmefc_ls_req *lsreq;
+ struct nvmet_fc_ls_iod *iod;
+ int i;
+
+ iod = tgtport->iod;
+ for (i = 0; i < NVMET_LS_CTX_COUNT; iod++, i++)
+ cancel_work(&iod->work);
+
+ /*
+ * After this point the connection is lost and thus any pending
+ * request can't be processed by the normal completion path. This
+ * is likely a request from nvmet_fc_send_ls_req_async.
+ */
+ while ((lsop = list_first_entry_or_null(&tgtport->ls_req_list,
+ struct nvmet_fc_ls_req_op, lsreq_list))) {
+ list_del(&lsop->lsreq_list);
+
+ if (!lsop->req_queued)
+ continue;
+
+ lsreq = &lsop->ls_req;
+ fc_dma_unmap_single(tgtport->dev, lsreq->rqstdma,
+ (lsreq->rqstlen + lsreq->rsplen),
+ DMA_BIDIRECTIONAL);
+ nvmet_fc_tgtport_put(tgtport);
+ kfree(lsop);
+ }
+}
+
/**
* nvmet_fc_unregister_targetport - transport entry point called by an
* LLDD to deregister/remove a previously
@@ -1608,13 +1641,7 @@ nvmet_fc_unregister_targetport(struct nvmet_fc_target_port *target_port)
flush_workqueue(nvmet_wq);
- /*
- * should terminate LS's as well. However, LS's will be generated
- * at the tail end of association termination, so they likely don't
- * exist yet. And even if they did, it's worthwhile to just let
- * them finish and targetport ref counting will clean things up.
- */
-
+ nvmet_fc_free_pending_reqs(tgtport);
nvmet_fc_tgtport_put(tgtport);
return 0;
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PATCH v5 13/14] nvmet-fc: take tgtport refs for portentry
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
` (11 preceding siblings ...)
2025-04-23 13:21 ` [PATCH v5 12/14] nvmet-fc: free pending reqs on tgtport unregister Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-04-23 13:21 ` [PATCH v5 14/14] nvme-fc: do not reference lsrsp after failure Daniel Wagner
2025-05-06 16:36 ` [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
14 siblings, 0 replies; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
Ensure that the tgtport is not going away as long portentry has a
pointer on it.
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/target/fc.c | 44 +++++++++++++++++++++++++++++++++++++++-----
1 file changed, 39 insertions(+), 5 deletions(-)
diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c
index 75ddb7425605dd6623db38a133b63e201592354c..2835772677f9486324ef77d9021b49f8d153e916 100644
--- a/drivers/nvme/target/fc.c
+++ b/drivers/nvme/target/fc.c
@@ -1254,6 +1254,7 @@ nvmet_fc_portentry_bind(struct nvmet_fc_tgtport *tgtport,
{
lockdep_assert_held(&nvmet_fc_tgtlock);
+ nvmet_fc_tgtport_get(tgtport);
pe->tgtport = tgtport;
tgtport->pe = pe;
@@ -1273,8 +1274,10 @@ nvmet_fc_portentry_unbind(struct nvmet_fc_port_entry *pe)
unsigned long flags;
spin_lock_irqsave(&nvmet_fc_tgtlock, flags);
- if (pe->tgtport)
+ if (pe->tgtport) {
+ nvmet_fc_tgtport_put(pe->tgtport);
pe->tgtport->pe = NULL;
+ }
list_del(&pe->pe_list);
spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags);
}
@@ -1292,8 +1295,10 @@ nvmet_fc_portentry_unbind_tgt(struct nvmet_fc_tgtport *tgtport)
spin_lock_irqsave(&nvmet_fc_tgtlock, flags);
pe = tgtport->pe;
- if (pe)
+ if (pe) {
+ nvmet_fc_tgtport_put(pe->tgtport);
pe->tgtport = NULL;
+ }
tgtport->pe = NULL;
spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags);
}
@@ -1316,6 +1321,9 @@ nvmet_fc_portentry_rebind_tgt(struct nvmet_fc_tgtport *tgtport)
list_for_each_entry(pe, &nvmet_fc_portentry_list, pe_list) {
if (tgtport->fc_target_port.node_name == pe->node_name &&
tgtport->fc_target_port.port_name == pe->port_name) {
+ if (!nvmet_fc_tgtport_get(tgtport))
+ continue;
+
WARN_ON(pe->tgtport);
tgtport->pe = pe;
pe->tgtport = tgtport;
@@ -2887,12 +2895,17 @@ nvmet_fc_add_port(struct nvmet_port *port)
list_for_each_entry(tgtport, &nvmet_fc_target_list, tgt_list) {
if ((tgtport->fc_target_port.node_name == traddr.nn) &&
(tgtport->fc_target_port.port_name == traddr.pn)) {
+ if (!nvmet_fc_tgtport_get(tgtport))
+ continue;
+
/* a FC port can only be 1 nvmet port id */
if (!tgtport->pe) {
nvmet_fc_portentry_bind(tgtport, pe, port);
ret = 0;
} else
ret = -EALREADY;
+
+ nvmet_fc_tgtport_put(tgtport);
break;
}
}
@@ -2908,11 +2921,21 @@ static void
nvmet_fc_remove_port(struct nvmet_port *port)
{
struct nvmet_fc_port_entry *pe = port->priv;
+ struct nvmet_fc_tgtport *tgtport = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&nvmet_fc_tgtlock, flags);
+ if (pe->tgtport && nvmet_fc_tgtport_get(pe->tgtport))
+ tgtport = pe->tgtport;
+ spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags);
nvmet_fc_portentry_unbind(pe);
- /* terminate any outstanding associations */
- __nvmet_fc_free_assocs(pe->tgtport);
+ if (tgtport) {
+ /* terminate any outstanding associations */
+ __nvmet_fc_free_assocs(tgtport);
+ nvmet_fc_tgtport_put(tgtport);
+ }
kfree(pe);
}
@@ -2921,10 +2944,21 @@ static void
nvmet_fc_discovery_chg(struct nvmet_port *port)
{
struct nvmet_fc_port_entry *pe = port->priv;
- struct nvmet_fc_tgtport *tgtport = pe->tgtport;
+ struct nvmet_fc_tgtport *tgtport = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&nvmet_fc_tgtlock, flags);
+ if (pe->tgtport && nvmet_fc_tgtport_get(pe->tgtport))
+ tgtport = pe->tgtport;
+ spin_unlock_irqrestore(&nvmet_fc_tgtlock, flags);
+
+ if (!tgtport)
+ return;
if (tgtport && tgtport->ops->discovery_event)
tgtport->ops->discovery_event(&tgtport->fc_target_port);
+
+ nvmet_fc_tgtport_put(tgtport);
}
static ssize_t
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* [PATCH v5 14/14] nvme-fc: do not reference lsrsp after failure
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
` (12 preceding siblings ...)
2025-04-23 13:21 ` [PATCH v5 13/14] nvmet-fc: take tgtport refs for portentry Daniel Wagner
@ 2025-04-23 13:21 ` Daniel Wagner
2025-05-06 16:36 ` [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
14 siblings, 0 replies; 34+ messages in thread
From: Daniel Wagner @ 2025-04-23 13:21 UTC (permalink / raw)
To: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni
Cc: Hannes Reinecke, Keith Busch, linux-nvme, linux-kernel,
Daniel Wagner
The lsrsp object is maintained by the LLDD. The lifetime of the lsrsp
object is implicit. Because there is no explicit cleanup/free call into
the LLDD, it is not safe to assume after xml_rsp_fails, that the lsrsp
is still valid. The LLDD could have freed the object already.
With the recent changes how fcloop tracks the resources, this is the
case. Thus don't access lsrsp after xml_rsp_fails.
Signed-off-by: Daniel Wagner <wagi@kernel.org>
---
drivers/nvme/host/fc.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 2257c3c96dd2da11097d2d0d4a5bb8ece1bebc6a..fdafa3e9e66fa9b8954209efa14d3b207cb8653c 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -1410,9 +1410,8 @@ nvme_fc_xmt_disconnect_assoc(struct nvme_fc_ctrl *ctrl)
}
static void
-nvme_fc_xmt_ls_rsp_done(struct nvmefc_ls_rsp *lsrsp)
+nvme_fc_xmt_ls_rsp_free(struct nvmefc_ls_rcv_op *lsop)
{
- struct nvmefc_ls_rcv_op *lsop = lsrsp->nvme_fc_private;
struct nvme_fc_rport *rport = lsop->rport;
struct nvme_fc_lport *lport = rport->lport;
unsigned long flags;
@@ -1433,6 +1432,14 @@ nvme_fc_xmt_ls_rsp_done(struct nvmefc_ls_rsp *lsrsp)
nvme_fc_rport_put(rport);
}
+static void
+nvme_fc_xmt_ls_rsp_done(struct nvmefc_ls_rsp *lsrsp)
+{
+ struct nvmefc_ls_rcv_op *lsop = lsrsp->nvme_fc_private;
+
+ nvme_fc_xmt_ls_rsp_free(lsop);
+}
+
static void
nvme_fc_xmt_ls_rsp(struct nvmefc_ls_rcv_op *lsop)
{
@@ -1450,7 +1457,7 @@ nvme_fc_xmt_ls_rsp(struct nvmefc_ls_rcv_op *lsop)
dev_warn(lport->dev,
"LLDD rejected LS RSP xmt: LS %d status %d\n",
w0->ls_cmd, ret);
- nvme_fc_xmt_ls_rsp_done(lsop->lsrsp);
+ nvme_fc_xmt_ls_rsp_free(lsop);
return;
}
}
--
2.49.0
^ permalink raw reply related [flat|nested] 34+ messages in thread* Re: [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting
2025-04-23 13:21 [PATCH v5 00/14] nvmet-fcloop: track resources via reference counting Daniel Wagner
` (13 preceding siblings ...)
2025-04-23 13:21 ` [PATCH v5 14/14] nvme-fc: do not reference lsrsp after failure Daniel Wagner
@ 2025-05-06 16:36 ` Daniel Wagner
14 siblings, 0 replies; 34+ messages in thread
From: Daniel Wagner @ 2025-05-06 16:36 UTC (permalink / raw)
To: linux-nvme
Cc: James Smart, Christoph Hellwig, Sagi Grimberg, Chaitanya Kulkarni,
Hannes Reinecke, Keith Busch, linux-kernel
On Wed, Apr 23, 2025 at 03:21:43PM +0200, Daniel Wagner wrote:
> Note blktests nvme/030 test is likely to fail if the
> 70-nvmf-autoconnect.rules is active. In this case two discovery are
> running in parallel and nvme-cli/libnvme gets out of sync. I don't see a
> problem in blktests, but maybe I am just blind:
>
> nvme/030 (tr=fc) (ensure the discovery generation counter is updated appropriately) [failed]
> runtime 1.843s ... 1.719s
> --- tests/nvme/030.out 2023-08-30 08:39:08.428409596 +0000
> +++ /tmp/blktests/nodev_tr_fc/nvme/030.out.bad 2025-04-10 10:56:05.146372112 +0000
> @@ -1,2 +1,6 @@
> Running nvme/030
> +Failed to open ctrl nvme1, errno 11
> +Failed to open ctrl nvme1, errno 11
> +failed to get discovery log: Bad file descriptor
It turns out that nvme/030 uncovered a bunch of bugs. First the kernel
returned EAGAIN consistently for a while and could easily reproduce it.
But after updating something it went away. I think the EAGAIN was issued
because in my test setup the udev rule is active and triggers a discover
(creates a discover ctrl) which runs in parallel with the test, also
running a discover. I think EAGAIN was alwasys there but it is hard to
hit.
I've added a workaround to handle EINTR to libnvme but after reading up
on signals, I came to the conclusion, nvme-cli needs to handle EAGAIN
and EINTR. The EINTR case might be entered with Ctrl-C and in this case
we want to terminate the loop. Installing a signal handler in a library
is a no go from my understanding:
https://github.com/linux-nvme/nvme-cli/pull/2797
After getting this out of the way, I figured out that nvmet-fc is not
able to handle more than on in flight async, and there is a nested
locking issue in nvme-fc.
The tests get more and more reliable, though I thought I saw a KASAN
report but now it's not reproducing. Yeah, everyone loves heisenbugs.
^ permalink raw reply [flat|nested] 34+ messages in thread