Refactor and fc_rport_set_marginal_state smp safe by holding
`shost->host_lock` around all `rport->port_state` accesses.

Call nvme_fc_modify_rport_fpin_state() when FC_PORTSTATE_MARGINAL is set
or cleared.  This allows the user to quickly set or clear the
NVME_CTRL_MARGINAL state from sysfs.

E.g.:

 echo "Marginal" > /sys/class/fc_remote_ports/rport-13:0-5/port_state
 echo "Online" > /sys/class/fc_remote_ports/rport-13:0-5/port_state

Note: nvme_fc_modify_rport_fpin_state() will only affect rports that
      have FC_PORT_ROLE_NVME_TARGET set.

Signed-off-by: John Meneghini <[email protected]>
---
 drivers/scsi/scsi_transport_fc.c | 46 +++++++++++++++++++++++++-------
 1 file changed, 37 insertions(+), 9 deletions(-)

diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index 4dc03cbaf3e2..811530037a1e 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -1305,34 +1305,62 @@ static ssize_t fc_rport_set_marginal_state(struct 
device *dev,
                                                const char *buf, size_t count)
 {
        struct fc_rport *rport = transport_class_to_rport(dev);
+       struct Scsi_Host *shost = rport_to_shost(rport);
+       u64 local_wwpn = fc_host_port_name(shost);
        enum fc_port_state port_state;
        int ret = 0;
+       unsigned long flags;
 
        ret = get_fc_port_state_match(buf, &port_state);
        if (ret)
                return -EINVAL;
-       if (port_state == FC_PORTSTATE_MARGINAL) {
+
+       spin_lock_irqsave(shost->host_lock, flags);
+
+       switch (port_state) {
+       case FC_PORTSTATE_MARGINAL:
                /*
                 * Change the state to Marginal only if the
                 * current rport state is Online
                 * Allow only Online->Marginal
                 */
-               if (rport->port_state == FC_PORTSTATE_ONLINE)
+               if (rport->port_state == FC_PORTSTATE_ONLINE) {
                        rport->port_state = port_state;
-               else if (port_state != rport->port_state)
-                       return -EINVAL;
-       } else if (port_state == FC_PORTSTATE_ONLINE) {
+                       spin_unlock_irqrestore(shost->host_lock, flags);
+#if (IS_ENABLED(CONFIG_NVME_FC))
+                       nvme_fc_modify_rport_fpin_state(local_wwpn,
+                                       rport->port_name, true);
+#endif
+                       return count;
+               }
+               break;
+
+       case FC_PORTSTATE_ONLINE:
                /*
                 * Change the state to Online only if the
                 * current rport state is Marginal
                 * Allow only Marginal->Online
                 */
-               if (rport->port_state == FC_PORTSTATE_MARGINAL)
+               if (rport->port_state == FC_PORTSTATE_MARGINAL) {
                        rport->port_state = port_state;
-               else if (port_state != rport->port_state)
-                       return -EINVAL;
-       } else
+                       spin_unlock_irqrestore(shost->host_lock, flags);
+#if (IS_ENABLED(CONFIG_NVME_FC))
+                       nvme_fc_modify_rport_fpin_state(local_wwpn,
+                                       rport->port_name, false);
+#endif
+                       return count;
+               }
+               break;
+       default:
+               break;
+       }
+
+       if (port_state != rport->port_state) {
+               spin_unlock_irqrestore(shost->host_lock, flags);
                return -EINVAL;
+       }
+
+       spin_unlock_irqrestore(shost->host_lock, flags);
        return count;
 }
 
-- 
2.51.0


Reply via email to