|
|
@@ -220,6 +220,90 @@ static int __nvme_fc_del_ctrl(struct nvme_fc_ctrl *);
|
|
|
static void __nvme_fc_delete_hw_queue(struct nvme_fc_ctrl *,
|
|
|
struct nvme_fc_queue *, unsigned int);
|
|
|
|
|
|
+static void
|
|
|
+nvme_fc_free_lport(struct kref *ref)
|
|
|
+{
|
|
|
+ struct nvme_fc_lport *lport =
|
|
|
+ container_of(ref, struct nvme_fc_lport, ref);
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ WARN_ON(lport->localport.port_state != FC_OBJSTATE_DELETED);
|
|
|
+ WARN_ON(!list_empty(&lport->endp_list));
|
|
|
+
|
|
|
+ /* remove from transport list */
|
|
|
+ spin_lock_irqsave(&nvme_fc_lock, flags);
|
|
|
+ list_del(&lport->port_list);
|
|
|
+ spin_unlock_irqrestore(&nvme_fc_lock, flags);
|
|
|
+
|
|
|
+ /* let the LLDD know we've finished tearing it down */
|
|
|
+ lport->ops->localport_delete(&lport->localport);
|
|
|
+
|
|
|
+ ida_simple_remove(&nvme_fc_local_port_cnt, lport->localport.port_num);
|
|
|
+ ida_destroy(&lport->endp_cnt);
|
|
|
+
|
|
|
+ put_device(lport->dev);
|
|
|
+
|
|
|
+ kfree(lport);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+nvme_fc_lport_put(struct nvme_fc_lport *lport)
|
|
|
+{
|
|
|
+ kref_put(&lport->ref, nvme_fc_free_lport);
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+nvme_fc_lport_get(struct nvme_fc_lport *lport)
|
|
|
+{
|
|
|
+ return kref_get_unless_zero(&lport->ref);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static struct nvme_fc_lport *
|
|
|
+nvme_fc_attach_to_unreg_lport(struct nvme_fc_port_info *pinfo)
|
|
|
+{
|
|
|
+ struct nvme_fc_lport *lport;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&nvme_fc_lock, flags);
|
|
|
+
|
|
|
+ list_for_each_entry(lport, &nvme_fc_lport_list, port_list) {
|
|
|
+ if (lport->localport.node_name != pinfo->node_name ||
|
|
|
+ lport->localport.port_name != pinfo->port_name)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (lport->localport.port_state != FC_OBJSTATE_DELETED) {
|
|
|
+ lport = ERR_PTR(-EEXIST);
|
|
|
+ goto out_done;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!nvme_fc_lport_get(lport)) {
|
|
|
+ /*
|
|
|
+ * fails if ref cnt already 0. If so,
|
|
|
+ * act as if lport already deleted
|
|
|
+ */
|
|
|
+ lport = NULL;
|
|
|
+ goto out_done;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* resume the lport */
|
|
|
+
|
|
|
+ lport->localport.port_role = pinfo->port_role;
|
|
|
+ lport->localport.port_id = pinfo->port_id;
|
|
|
+ lport->localport.port_state = FC_OBJSTATE_ONLINE;
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&nvme_fc_lock, flags);
|
|
|
+
|
|
|
+ return lport;
|
|
|
+ }
|
|
|
+
|
|
|
+ lport = NULL;
|
|
|
+
|
|
|
+out_done:
|
|
|
+ spin_unlock_irqrestore(&nvme_fc_lock, flags);
|
|
|
+
|
|
|
+ return lport;
|
|
|
+}
|
|
|
|
|
|
/**
|
|
|
* nvme_fc_register_localport - transport entry point called by an
|
|
|
@@ -257,6 +341,28 @@ nvme_fc_register_localport(struct nvme_fc_port_info *pinfo,
|
|
|
goto out_reghost_failed;
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * look to see if there is already a localport that had been
|
|
|
+ * deregistered and in the process of waiting for all the
|
|
|
+ * references to fully be removed. If the references haven't
|
|
|
+ * expired, we can simply re-enable the localport. Remoteports
|
|
|
+ * and controller reconnections should resume naturally.
|
|
|
+ */
|
|
|
+ newrec = nvme_fc_attach_to_unreg_lport(pinfo);
|
|
|
+
|
|
|
+ /* found an lport, but something about its state is bad */
|
|
|
+ if (IS_ERR(newrec)) {
|
|
|
+ ret = PTR_ERR(newrec);
|
|
|
+ goto out_reghost_failed;
|
|
|
+
|
|
|
+ /* found existing lport, which was resumed */
|
|
|
+ } else if (newrec) {
|
|
|
+ *portptr = &newrec->localport;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* nothing found - allocate a new localport struct */
|
|
|
+
|
|
|
newrec = kmalloc((sizeof(*newrec) + template->local_priv_sz),
|
|
|
GFP_KERNEL);
|
|
|
if (!newrec) {
|
|
|
@@ -310,44 +416,6 @@ out_reghost_failed:
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(nvme_fc_register_localport);
|
|
|
|
|
|
-static void
|
|
|
-nvme_fc_free_lport(struct kref *ref)
|
|
|
-{
|
|
|
- struct nvme_fc_lport *lport =
|
|
|
- container_of(ref, struct nvme_fc_lport, ref);
|
|
|
- unsigned long flags;
|
|
|
-
|
|
|
- WARN_ON(lport->localport.port_state != FC_OBJSTATE_DELETED);
|
|
|
- WARN_ON(!list_empty(&lport->endp_list));
|
|
|
-
|
|
|
- /* remove from transport list */
|
|
|
- spin_lock_irqsave(&nvme_fc_lock, flags);
|
|
|
- list_del(&lport->port_list);
|
|
|
- spin_unlock_irqrestore(&nvme_fc_lock, flags);
|
|
|
-
|
|
|
- /* let the LLDD know we've finished tearing it down */
|
|
|
- lport->ops->localport_delete(&lport->localport);
|
|
|
-
|
|
|
- ida_simple_remove(&nvme_fc_local_port_cnt, lport->localport.port_num);
|
|
|
- ida_destroy(&lport->endp_cnt);
|
|
|
-
|
|
|
- put_device(lport->dev);
|
|
|
-
|
|
|
- kfree(lport);
|
|
|
-}
|
|
|
-
|
|
|
-static void
|
|
|
-nvme_fc_lport_put(struct nvme_fc_lport *lport)
|
|
|
-{
|
|
|
- kref_put(&lport->ref, nvme_fc_free_lport);
|
|
|
-}
|
|
|
-
|
|
|
-static int
|
|
|
-nvme_fc_lport_get(struct nvme_fc_lport *lport)
|
|
|
-{
|
|
|
- return kref_get_unless_zero(&lport->ref);
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* nvme_fc_unregister_localport - transport entry point called by an
|
|
|
* LLDD to deregister/remove a previously
|