[SCSI] ibmvfc: Fix rport add/delete race resulting in oops
authorBrian King <brking@linux.vnet.ibm.com>
Thu, 5 Aug 2010 21:38:31 +0000 (16:38 -0500)
committerJames Bottomley <James.Bottomley@suse.de>
Fri, 6 Aug 2010 17:26:33 +0000 (12:26 -0500)
Commit 43c8da907ccc656935d1085701f4db83385d8a59 introduced a race
condition which can occur when adding/deleting rports. There are
two possible threads now that can be deleting rports in the ibmvfc
driver, which can result in list_del being called twice, resulting
in an oops. This patch adds a new state to the ibmvfc_target struct
to indicate the target has been removed from the list and is in
the process of being deleted.

Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
drivers/scsi/ibmvscsi/ibmvfc.c
drivers/scsi/ibmvscsi/ibmvfc.h

index bd96cecaa61902f82734a8aad5399268eb05a819..a13db590842692df2a02095f906a46ccdf49bbee 100644 (file)
@@ -433,6 +433,9 @@ static void ibmvfc_set_tgt_action(struct ibmvfc_target *tgt,
 {
        switch (tgt->action) {
        case IBMVFC_TGT_ACTION_DEL_RPORT:
+               if (action == IBMVFC_TGT_ACTION_DELETED_RPORT)
+                       tgt->action = action;
+       case IBMVFC_TGT_ACTION_DELETED_RPORT:
                break;
        default:
                if (action == IBMVFC_TGT_ACTION_DEL_RPORT)
@@ -4193,11 +4196,15 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
        if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
                tgt_dbg(tgt, "Deleting rport\n");
                list_del(&tgt->queue);
+               ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
                spin_unlock_irqrestore(vhost->host->host_lock, flags);
                fc_remote_port_delete(rport);
                del_timer_sync(&tgt->timer);
                kref_put(&tgt->kref, ibmvfc_release_tgt);
                return;
+       } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
+               spin_unlock_irqrestore(vhost->host->host_lock, flags);
+               return;
        }
 
        if (rport) {
@@ -4297,6 +4304,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
                                rport = tgt->rport;
                                tgt->rport = NULL;
                                list_del(&tgt->queue);
+                               ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
                                spin_unlock_irqrestore(vhost->host->host_lock, flags);
                                if (rport)
                                        fc_remote_port_delete(rport);
index d7e8dcd906504b157539ba1751d14d1a50bf3d82..af48172112fa72dad9cdbfad952e85429e3f7518 100644 (file)
@@ -597,6 +597,7 @@ enum ibmvfc_target_action {
        IBMVFC_TGT_ACTION_INIT,
        IBMVFC_TGT_ACTION_INIT_WAIT,
        IBMVFC_TGT_ACTION_DEL_RPORT,
+       IBMVFC_TGT_ACTION_DELETED_RPORT,
 };
 
 struct ibmvfc_target {