aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/fcoe/fcoe.c
diff options
context:
space:
mode:
authorNeil Horman <nhorman@tuxdriver.com>2012-03-09 17:49:48 -0500
committerJames Bottomley <JBottomley@Parallels.com>2012-03-28 04:02:26 -0400
commit5e70c4c43e559ea6a1bf1edc0eb7d284ea7f16b4 (patch)
tree48cf08ae09bdf23e9a48a10821ca5223f25ab74f /drivers/scsi/fcoe/fcoe.c
parentb08c1856b4d4295040ec72f15427588087369220 (diff)
[SCSI] fcoe: Ensure fcoe_recv_frame is always called in process context
commit 859b7b649ab58ee5cbfb761491317d5b315c1b0f introduced the ability to call fcoe_recv_frame in softirq context. While this is beneficial to performance, its not safe to do, as it breaks the serialization of access to the lport structure (i.e. when an fcoe interface is being torn down, theres no way to serialize the teardown effort with the completion of receieve operations occuring in softirq context. As a result, lport (and other) data structures can be read and modified in parallel leading to corruption. Most notable is the vport list, which is protected by a mutex, that will cause a panic if a softirq receive while said mutex is locked. Additionaly, the ema_list, discussed here: http://lists.open-fcoe.org/pipermail/devel/2012-February/011947.html Can be corrupted if a list traversal occurs in softirq context at the same time as a list delete in process context. And generally the lport state variables will not be stable, and may lead to unpredictable results. The most direct fix is to remove the bits from the above commit that allowed fcoe_recv_frame to be called in softirq context. We just force all frames to be handled by the per-cpu rx threads. This will allow the fcoe_if_destroy's use of fcoe_percpu_clean to function properly, ensuring that no frames are being received while the lport is being torn down. Signed-off-by: Neil Horman <nhorman@tuxdriver.com> Reviewed-by: Vasu Dev <vasu.dev@intel.com> Signed-off-by: Robert Love <robert.w.love@intel.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/fcoe/fcoe.c')
-rw-r--r--drivers/scsi/fcoe/fcoe.c27
1 files changed, 10 insertions, 17 deletions
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 278958157e2..22ae29520d6 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1463,24 +1463,17 @@ static int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
1463 * so we're free to queue skbs into it's queue. 1463 * so we're free to queue skbs into it's queue.
1464 */ 1464 */
1465 1465
1466 /* If this is a SCSI-FCP frame, and this is already executing on the 1466 /*
1467 * correct CPU, and the queue for this CPU is empty, then go ahead 1467 * Note: We used to have a set of conditions under which we would
1468 * and process the frame directly in the softirq context. 1468 * call fcoe_recv_frame directly, rather than queuing to the rx list
1469 * This lets us process completions without context switching from the 1469 * as it could save a few cycles, but doing so is prohibited, as
1470 * NET_RX softirq, to our receive processing thread, and then back to 1470 * fcoe_recv_frame has several paths that may sleep, which is forbidden
1471 * BLOCK softirq context. 1471 * in softirq context.
1472 */ 1472 */
1473 if (fh->fh_type == FC_TYPE_FCP && 1473 __skb_queue_tail(&fps->fcoe_rx_list, skb);
1474 cpu == smp_processor_id() && 1474 if (fps->fcoe_rx_list.qlen == 1)
1475 skb_queue_empty(&fps->fcoe_rx_list)) { 1475 wake_up_process(fps->thread);
1476 spin_unlock_bh(&fps->fcoe_rx_list.lock); 1476 spin_unlock_bh(&fps->fcoe_rx_list.lock);
1477 fcoe_recv_frame(skb);
1478 } else {
1479 __skb_queue_tail(&fps->fcoe_rx_list, skb);
1480 if (fps->fcoe_rx_list.qlen == 1)
1481 wake_up_process(fps->thread);
1482 spin_unlock_bh(&fps->fcoe_rx_list.lock);
1483 }
1484 1477
1485 return 0; 1478 return 0;
1486err: 1479err: