diff options
Diffstat (limited to 'drivers/scsi/fcoe/fcoe.c')
-rw-r--r-- | drivers/scsi/fcoe/fcoe.c | 245 |
1 files changed, 135 insertions, 110 deletions
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 4a43b74c0d27..32298ed60614 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c | |||
@@ -109,6 +109,7 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *, | |||
109 | struct fc_frame *, | 109 | struct fc_frame *, |
110 | void *), | 110 | void *), |
111 | void *, u32 timeout); | 111 | void *, u32 timeout); |
112 | static void fcoe_recv_frame(struct sk_buff *skb); | ||
112 | 113 | ||
113 | module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR); | 114 | module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR); |
114 | __MODULE_PARM_TYPE(create, "string"); | 115 | __MODULE_PARM_TYPE(create, "string"); |
@@ -1241,11 +1242,25 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, | |||
1241 | * this skb. We also have this receive thread locked, | 1242 | * this skb. We also have this receive thread locked, |
1242 | * so we're free to queue skbs into it's queue. | 1243 | * so we're free to queue skbs into it's queue. |
1243 | */ | 1244 | */ |
1244 | __skb_queue_tail(&fps->fcoe_rx_list, skb); | ||
1245 | if (fps->fcoe_rx_list.qlen == 1) | ||
1246 | wake_up_process(fps->thread); | ||
1247 | 1245 | ||
1248 | spin_unlock_bh(&fps->fcoe_rx_list.lock); | 1246 | /* If this is a SCSI-FCP frame, and this is already executing on the |
1247 | * correct CPU, and the queue for this CPU is empty, then go ahead | ||
1248 | * and process the frame directly in the softirq context. | ||
1249 | * This lets us process completions without context switching from the | ||
1250 | * NET_RX softirq, to our receive processing thread, and then back to | ||
1251 | * BLOCK softirq context. | ||
1252 | */ | ||
1253 | if (fh->fh_type == FC_TYPE_FCP && | ||
1254 | cpu == smp_processor_id() && | ||
1255 | skb_queue_empty(&fps->fcoe_rx_list)) { | ||
1256 | spin_unlock_bh(&fps->fcoe_rx_list.lock); | ||
1257 | fcoe_recv_frame(skb); | ||
1258 | } else { | ||
1259 | __skb_queue_tail(&fps->fcoe_rx_list, skb); | ||
1260 | if (fps->fcoe_rx_list.qlen == 1) | ||
1261 | wake_up_process(fps->thread); | ||
1262 | spin_unlock_bh(&fps->fcoe_rx_list.lock); | ||
1263 | } | ||
1249 | 1264 | ||
1250 | return 0; | 1265 | return 0; |
1251 | err: | 1266 | err: |
@@ -1503,26 +1518,134 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb) | |||
1503 | } | 1518 | } |
1504 | 1519 | ||
1505 | /** | 1520 | /** |
1506 | * fcoe_percpu_receive_thread() - The per-CPU packet receive thread | 1521 | * fcoe_recv_frame() - process a single received frame |
1507 | * @arg: The per-CPU context | 1522 | * @skb: frame to process |
1508 | * | ||
1509 | * Return: 0 for success | ||
1510 | */ | 1523 | */ |
1511 | int fcoe_percpu_receive_thread(void *arg) | 1524 | static void fcoe_recv_frame(struct sk_buff *skb) |
1512 | { | 1525 | { |
1513 | struct fcoe_percpu_s *p = arg; | ||
1514 | u32 fr_len; | 1526 | u32 fr_len; |
1515 | struct fc_lport *lport; | 1527 | struct fc_lport *lport; |
1516 | struct fcoe_rcv_info *fr; | 1528 | struct fcoe_rcv_info *fr; |
1517 | struct fcoe_dev_stats *stats; | 1529 | struct fcoe_dev_stats *stats; |
1518 | struct fc_frame_header *fh; | 1530 | struct fc_frame_header *fh; |
1519 | struct sk_buff *skb; | ||
1520 | struct fcoe_crc_eof crc_eof; | 1531 | struct fcoe_crc_eof crc_eof; |
1521 | struct fc_frame *fp; | 1532 | struct fc_frame *fp; |
1522 | u8 *mac = NULL; | 1533 | u8 *mac = NULL; |
1523 | struct fcoe_port *port; | 1534 | struct fcoe_port *port; |
1524 | struct fcoe_hdr *hp; | 1535 | struct fcoe_hdr *hp; |
1525 | 1536 | ||
1537 | fr = fcoe_dev_from_skb(skb); | ||
1538 | lport = fr->fr_dev; | ||
1539 | if (unlikely(!lport)) { | ||
1540 | if (skb->destructor != fcoe_percpu_flush_done) | ||
1541 | FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb"); | ||
1542 | kfree_skb(skb); | ||
1543 | return; | ||
1544 | } | ||
1545 | |||
1546 | FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d " | ||
1547 | "head:%p data:%p tail:%p end:%p sum:%d dev:%s", | ||
1548 | skb->len, skb->data_len, | ||
1549 | skb->head, skb->data, skb_tail_pointer(skb), | ||
1550 | skb_end_pointer(skb), skb->csum, | ||
1551 | skb->dev ? skb->dev->name : "<NULL>"); | ||
1552 | |||
1553 | /* | ||
1554 | * Save source MAC address before discarding header. | ||
1555 | */ | ||
1556 | port = lport_priv(lport); | ||
1557 | if (skb_is_nonlinear(skb)) | ||
1558 | skb_linearize(skb); /* not ideal */ | ||
1559 | mac = eth_hdr(skb)->h_source; | ||
1560 | |||
1561 | /* | ||
1562 | * Frame length checks and setting up the header pointers | ||
1563 | * was done in fcoe_rcv already. | ||
1564 | */ | ||
1565 | hp = (struct fcoe_hdr *) skb_network_header(skb); | ||
1566 | fh = (struct fc_frame_header *) skb_transport_header(skb); | ||
1567 | |||
1568 | stats = fc_lport_get_stats(lport); | ||
1569 | if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { | ||
1570 | if (stats->ErrorFrames < 5) | ||
1571 | printk(KERN_WARNING "fcoe: FCoE version " | ||
1572 | "mismatch: The frame has " | ||
1573 | "version %x, but the " | ||
1574 | "initiator supports version " | ||
1575 | "%x\n", FC_FCOE_DECAPS_VER(hp), | ||
1576 | FC_FCOE_VER); | ||
1577 | stats->ErrorFrames++; | ||
1578 | kfree_skb(skb); | ||
1579 | return; | ||
1580 | } | ||
1581 | |||
1582 | skb_pull(skb, sizeof(struct fcoe_hdr)); | ||
1583 | fr_len = skb->len - sizeof(struct fcoe_crc_eof); | ||
1584 | |||
1585 | stats->RxFrames++; | ||
1586 | stats->RxWords += fr_len / FCOE_WORD_TO_BYTE; | ||
1587 | |||
1588 | fp = (struct fc_frame *)skb; | ||
1589 | fc_frame_init(fp); | ||
1590 | fr_dev(fp) = lport; | ||
1591 | fr_sof(fp) = hp->fcoe_sof; | ||
1592 | |||
1593 | /* Copy out the CRC and EOF trailer for access */ | ||
1594 | if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) { | ||
1595 | kfree_skb(skb); | ||
1596 | return; | ||
1597 | } | ||
1598 | fr_eof(fp) = crc_eof.fcoe_eof; | ||
1599 | fr_crc(fp) = crc_eof.fcoe_crc32; | ||
1600 | if (pskb_trim(skb, fr_len)) { | ||
1601 | kfree_skb(skb); | ||
1602 | return; | ||
1603 | } | ||
1604 | |||
1605 | /* | ||
1606 | * We only check CRC if no offload is available and if it is | ||
1607 | * it's solicited data, in which case, the FCP layer would | ||
1608 | * check it during the copy. | ||
1609 | */ | ||
1610 | if (lport->crc_offload && | ||
1611 | skb->ip_summed == CHECKSUM_UNNECESSARY) | ||
1612 | fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; | ||
1613 | else | ||
1614 | fr_flags(fp) |= FCPHF_CRC_UNCHECKED; | ||
1615 | |||
1616 | fh = fc_frame_header_get(fp); | ||
1617 | if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && | ||
1618 | fh->fh_type == FC_TYPE_FCP) { | ||
1619 | fc_exch_recv(lport, fp); | ||
1620 | return; | ||
1621 | } | ||
1622 | if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { | ||
1623 | if (le32_to_cpu(fr_crc(fp)) != | ||
1624 | ~crc32(~0, skb->data, fr_len)) { | ||
1625 | if (stats->InvalidCRCCount < 5) | ||
1626 | printk(KERN_WARNING "fcoe: dropping " | ||
1627 | "frame with CRC error\n"); | ||
1628 | stats->InvalidCRCCount++; | ||
1629 | stats->ErrorFrames++; | ||
1630 | fc_frame_free(fp); | ||
1631 | return; | ||
1632 | } | ||
1633 | fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; | ||
1634 | } | ||
1635 | fc_exch_recv(lport, fp); | ||
1636 | } | ||
1637 | |||
1638 | /** | ||
1639 | * fcoe_percpu_receive_thread() - The per-CPU packet receive thread | ||
1640 | * @arg: The per-CPU context | ||
1641 | * | ||
1642 | * Return: 0 for success | ||
1643 | */ | ||
1644 | int fcoe_percpu_receive_thread(void *arg) | ||
1645 | { | ||
1646 | struct fcoe_percpu_s *p = arg; | ||
1647 | struct sk_buff *skb; | ||
1648 | |||
1526 | set_user_nice(current, -20); | 1649 | set_user_nice(current, -20); |
1527 | 1650 | ||
1528 | while (!kthread_should_stop()) { | 1651 | while (!kthread_should_stop()) { |
@@ -1538,105 +1661,7 @@ int fcoe_percpu_receive_thread(void *arg) | |||
1538 | spin_lock_bh(&p->fcoe_rx_list.lock); | 1661 | spin_lock_bh(&p->fcoe_rx_list.lock); |
1539 | } | 1662 | } |
1540 | spin_unlock_bh(&p->fcoe_rx_list.lock); | 1663 | spin_unlock_bh(&p->fcoe_rx_list.lock); |
1541 | fr = fcoe_dev_from_skb(skb); | 1664 | fcoe_recv_frame(skb); |
1542 | lport = fr->fr_dev; | ||
1543 | if (unlikely(!lport)) { | ||
1544 | if (skb->destructor != fcoe_percpu_flush_done) | ||
1545 | FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb"); | ||
1546 | kfree_skb(skb); | ||
1547 | continue; | ||
1548 | } | ||
1549 | |||
1550 | FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d " | ||
1551 | "head:%p data:%p tail:%p end:%p sum:%d dev:%s", | ||
1552 | skb->len, skb->data_len, | ||
1553 | skb->head, skb->data, skb_tail_pointer(skb), | ||
1554 | skb_end_pointer(skb), skb->csum, | ||
1555 | skb->dev ? skb->dev->name : "<NULL>"); | ||
1556 | |||
1557 | /* | ||
1558 | * Save source MAC address before discarding header. | ||
1559 | */ | ||
1560 | port = lport_priv(lport); | ||
1561 | if (skb_is_nonlinear(skb)) | ||
1562 | skb_linearize(skb); /* not ideal */ | ||
1563 | mac = eth_hdr(skb)->h_source; | ||
1564 | |||
1565 | /* | ||
1566 | * Frame length checks and setting up the header pointers | ||
1567 | * was done in fcoe_rcv already. | ||
1568 | */ | ||
1569 | hp = (struct fcoe_hdr *) skb_network_header(skb); | ||
1570 | fh = (struct fc_frame_header *) skb_transport_header(skb); | ||
1571 | |||
1572 | stats = fc_lport_get_stats(lport); | ||
1573 | if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { | ||
1574 | if (stats->ErrorFrames < 5) | ||
1575 | printk(KERN_WARNING "fcoe: FCoE version " | ||
1576 | "mismatch: The frame has " | ||
1577 | "version %x, but the " | ||
1578 | "initiator supports version " | ||
1579 | "%x\n", FC_FCOE_DECAPS_VER(hp), | ||
1580 | FC_FCOE_VER); | ||
1581 | stats->ErrorFrames++; | ||
1582 | kfree_skb(skb); | ||
1583 | continue; | ||
1584 | } | ||
1585 | |||
1586 | skb_pull(skb, sizeof(struct fcoe_hdr)); | ||
1587 | fr_len = skb->len - sizeof(struct fcoe_crc_eof); | ||
1588 | |||
1589 | stats->RxFrames++; | ||
1590 | stats->RxWords += fr_len / FCOE_WORD_TO_BYTE; | ||
1591 | |||
1592 | fp = (struct fc_frame *)skb; | ||
1593 | fc_frame_init(fp); | ||
1594 | fr_dev(fp) = lport; | ||
1595 | fr_sof(fp) = hp->fcoe_sof; | ||
1596 | |||
1597 | /* Copy out the CRC and EOF trailer for access */ | ||
1598 | if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) { | ||
1599 | kfree_skb(skb); | ||
1600 | continue; | ||
1601 | } | ||
1602 | fr_eof(fp) = crc_eof.fcoe_eof; | ||
1603 | fr_crc(fp) = crc_eof.fcoe_crc32; | ||
1604 | if (pskb_trim(skb, fr_len)) { | ||
1605 | kfree_skb(skb); | ||
1606 | continue; | ||
1607 | } | ||
1608 | |||
1609 | /* | ||
1610 | * We only check CRC if no offload is available and if it is | ||
1611 | * it's solicited data, in which case, the FCP layer would | ||
1612 | * check it during the copy. | ||
1613 | */ | ||
1614 | if (lport->crc_offload && | ||
1615 | skb->ip_summed == CHECKSUM_UNNECESSARY) | ||
1616 | fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; | ||
1617 | else | ||
1618 | fr_flags(fp) |= FCPHF_CRC_UNCHECKED; | ||
1619 | |||
1620 | fh = fc_frame_header_get(fp); | ||
1621 | if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && | ||
1622 | fh->fh_type == FC_TYPE_FCP) { | ||
1623 | fc_exch_recv(lport, fp); | ||
1624 | continue; | ||
1625 | } | ||
1626 | if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { | ||
1627 | if (le32_to_cpu(fr_crc(fp)) != | ||
1628 | ~crc32(~0, skb->data, fr_len)) { | ||
1629 | if (stats->InvalidCRCCount < 5) | ||
1630 | printk(KERN_WARNING "fcoe: dropping " | ||
1631 | "frame with CRC error\n"); | ||
1632 | stats->InvalidCRCCount++; | ||
1633 | stats->ErrorFrames++; | ||
1634 | fc_frame_free(fp); | ||
1635 | continue; | ||
1636 | } | ||
1637 | fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; | ||
1638 | } | ||
1639 | fc_exch_recv(lport, fp); | ||
1640 | } | 1665 | } |
1641 | return 0; | 1666 | return 0; |
1642 | } | 1667 | } |