/* * This function passes incoming fabric frames to libFC */ void fnic_handle_frame(struct work_struct *work) { struct fnic *fnic = container_of(work, struct fnic, frame_work); struct fc_lport *lp = fnic->lport; unsigned long flags; struct sk_buff *skb; struct fc_frame *fp; while ((skb = skb_dequeue(&fnic->frame_queue))) { spin_lock_irqsave(&fnic->fnic_lock, flags); if (fnic->stop_rx_link_events) { spin_unlock_irqrestore(&fnic->fnic_lock, flags); dev_kfree_skb(skb); return; } fp = (struct fc_frame *)skb; /* if Flogi resp frame, register the address */ if (fr_flags(fp)) { vnic_dev_add_addr(fnic->vdev, fnic->data_src_addr); fr_flags(fp) = 0; } spin_unlock_irqrestore(&fnic->fnic_lock, flags); fc_exch_recv(lp, lp->emp, fp); } }
/* * Check the CRC in a frame. */ u32 fc_frame_crc_check(struct fc_frame *fp) { u32 crc; u32 error; const u8 *bp; unsigned int len; WARN_ON(!fc_frame_is_linear(fp)); fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; len = (fr_len(fp) + 3) & ~3; /* round up length to include fill */ bp = (const u8 *) fr_hdr(fp); crc = ~crc32(~0, bp, len); error = crc ^ fr_crc(fp); return error; }
static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc *cq_desc, struct vnic_rq_buf *buf, int skipped __attribute__((unused)), void *opaque) { struct fnic *fnic = vnic_dev_priv(rq->vdev); struct sk_buff *skb; struct fc_frame *fp; unsigned int eth_hdrs_stripped; u8 type, color, eop, sop, ingress_port, vlan_stripped; u8 fcoe = 0, fcoe_sof, fcoe_eof; u8 fcoe_fc_crc_ok = 1, fcoe_enc_error = 0; u8 tcp_udp_csum_ok, udp, tcp, ipv4_csum_ok; u8 ipv6, ipv4, ipv4_fragment, rss_type, csum_not_calc; u8 fcs_ok = 1, packet_error = 0; u16 q_number, completed_index, bytes_written = 0, vlan, checksum; u32 rss_hash; u16 exchange_id, tmpl; u8 sof = 0; u8 eof = 0; u32 fcp_bytes_written = 0; unsigned long flags; pci_unmap_single(fnic->pdev, buf->dma_addr, buf->len, PCI_DMA_FROMDEVICE); skb = buf->os_buf; buf->os_buf = NULL; cq_desc_dec(cq_desc, &type, &color, &q_number, &completed_index); if (type == CQ_DESC_TYPE_RQ_FCP) { cq_fcp_rq_desc_dec((struct cq_fcp_rq_desc *)cq_desc, &type, &color, &q_number, &completed_index, &eop, &sop, &fcoe_fc_crc_ok, &exchange_id, &tmpl, &fcp_bytes_written, &sof, &eof, &ingress_port, &packet_error, &fcoe_enc_error, &fcs_ok, &vlan_stripped, &vlan); eth_hdrs_stripped = 1; } else if (type == CQ_DESC_TYPE_RQ_ENET) { cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc, &type, &color, &q_number, &completed_index, &ingress_port, &fcoe, &eop, &sop, &rss_type, &csum_not_calc, &rss_hash, &bytes_written, &packet_error, &vlan_stripped, &vlan, &checksum, &fcoe_sof, &fcoe_fc_crc_ok, &fcoe_enc_error, &fcoe_eof, &tcp_udp_csum_ok, &udp, &tcp, &ipv4_csum_ok, &ipv6, &ipv4, &ipv4_fragment, &fcs_ok); eth_hdrs_stripped = 0; } else { /* wrong CQ type*/ shost_printk(KERN_ERR, fnic->lport->host, "fnic rq_cmpl wrong cq type x%x\n", type); goto drop; } if (!fcs_ok || packet_error || !fcoe_fc_crc_ok || fcoe_enc_error) { FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "fnic rq_cmpl fcoe x%x fcsok x%x" " pkterr x%x fcoe_fc_crc_ok x%x, fcoe_enc_err" " x%x\n", fcoe, fcs_ok, packet_error, fcoe_fc_crc_ok, fcoe_enc_error); goto drop; } if (eth_hdrs_stripped) fnic_import_rq_fc_frame(skb, fcp_bytes_written, sof, eof); else if (fnic_import_rq_eth_pkt(skb, bytes_written)) goto drop; fp = (struct fc_frame *)skb; /* * If frame is an ELS response that matches the cached FLOGI OX_ID, * and is accept, issue flogi_reg_request copy wq request to firmware * to register the S_ID and determine whether FC_OUI mode or GW mode. */ if (is_matching_flogi_resp_frame(fnic, fp)) { if (!eth_hdrs_stripped) { if (fc_frame_payload_op(fp) == ELS_LS_ACC) { fnic_handle_flogi_resp(fnic, fp); return; } /* * Recd. Flogi reject. No point registering * with fw, but forward to libFC */ goto forward; } goto drop; } if (!eth_hdrs_stripped) goto drop; forward: spin_lock_irqsave(&fnic->fnic_lock, flags); if (fnic->stop_rx_link_events) { spin_unlock_irqrestore(&fnic->fnic_lock, flags); goto drop; } /* Use fr_flags to indicate whether succ. flogi resp or not */ fr_flags(fp) = 0; fr_dev(fp) = fnic->lport; spin_unlock_irqrestore(&fnic->fnic_lock, flags); skb_queue_tail(&fnic->frame_queue, skb); queue_work(fnic_event_queue, &fnic->frame_work); return; drop: dev_kfree_skb_irq(skb); }