void fnic_handle_link(struct work_struct *work) { struct fnic *fnic = container_of(work, struct fnic, link_work); unsigned long flags; int old_link_status; u32 old_link_down_cnt; spin_lock_irqsave(&fnic->fnic_lock, flags); if (fnic->stop_rx_link_events) { spin_unlock_irqrestore(&fnic->fnic_lock, flags); return; } old_link_down_cnt = fnic->link_down_cnt; old_link_status = fnic->link_status; fnic->link_status = vnic_dev_link_status(fnic->vdev); fnic->link_down_cnt = vnic_dev_link_down_cnt(fnic->vdev); if (old_link_status == fnic->link_status) { if (!fnic->link_status) spin_unlock_irqrestore(&fnic->fnic_lock, flags); else { if (old_link_down_cnt != fnic->link_down_cnt) { fnic->lport->host_stats.link_failure_count++; spin_unlock_irqrestore(&fnic->fnic_lock, flags); FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n"); fcoe_ctlr_link_down(&fnic->ctlr); FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n"); fcoe_ctlr_link_up(&fnic->ctlr); } else spin_unlock_irqrestore(&fnic->fnic_lock, flags); } } else if (fnic->link_status) { spin_unlock_irqrestore(&fnic->fnic_lock, flags); FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n"); fcoe_ctlr_link_up(&fnic->ctlr); } else { fnic->lport->host_stats.link_failure_count++; spin_unlock_irqrestore(&fnic->fnic_lock, flags); FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n"); fcoe_ctlr_link_down(&fnic->ctlr); } }
/* Process incoming FIP frames. */ void qedf_fip_recv(struct qedf_ctx *qedf, struct sk_buff *skb) { struct ethhdr *eth_hdr; struct fip_header *fiph; struct fip_desc *desc; struct fip_mac_desc *mp; struct fip_wwn_desc *wp; struct fip_vn_desc *vp; size_t rlen, dlen; uint32_t cvl_port_id; __u8 cvl_mac[ETH_ALEN]; u16 op; u8 sub; eth_hdr = (struct ethhdr *)skb_mac_header(skb); fiph = (struct fip_header *) ((void *)skb->data + 2 * ETH_ALEN + 2); op = ntohs(fiph->fip_op); sub = fiph->fip_subcode; QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FIP frame received: " "skb=%p fiph=%p source=%pM op=%x sub=%x", skb, fiph, eth_hdr->h_source, op, sub); if (qedf_dump_frames) print_hex_dump(KERN_WARNING, "fip ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb->len, false); /* Handle FIP VLAN resp in the driver */ if (op == FIP_OP_VLAN && sub == FIP_SC_VL_NOTE) { qedf_fcoe_process_vlan_resp(qedf, skb); qedf->vlan_hw_insert = 0; kfree_skb(skb); } else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK) { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Clear virtual " "link received.\n"); /* Check that an FCF has been selected by fcoe */ if (qedf->ctlr.sel_fcf == NULL) { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Dropping CVL since FCF has not been selected " "yet."); return; } cvl_port_id = 0; memset(cvl_mac, 0, ETH_ALEN); /* * We need to loop through the CVL descriptors to determine * if we want to reset the fcoe link */ rlen = ntohs(fiph->fip_dl_len) * FIP_BPW; desc = (struct fip_desc *)(fiph + 1); while (rlen >= sizeof(*desc)) { dlen = desc->fip_dlen * FIP_BPW; switch (desc->fip_dtype) { case FIP_DT_MAC: mp = (struct fip_mac_desc *)desc; QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "fd_mac=%pM\n", mp->fd_mac); ether_addr_copy(cvl_mac, mp->fd_mac); break; case FIP_DT_NAME: wp = (struct fip_wwn_desc *)desc; QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "fc_wwpn=%016llx.\n", get_unaligned_be64(&wp->fd_wwn)); break; case FIP_DT_VN_ID: vp = (struct fip_vn_desc *)desc; QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "fd_fc_id=%x.\n", ntoh24(vp->fd_fc_id)); cvl_port_id = ntoh24(vp->fd_fc_id); break; default: /* Ignore anything else */ break; } desc = (struct fip_desc *)((char *)desc + dlen); rlen -= dlen; } QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "cvl_port_id=%06x cvl_mac=%pM.\n", cvl_port_id, cvl_mac); if (cvl_port_id == qedf->lport->port_id && ether_addr_equal(cvl_mac, qedf->ctlr.sel_fcf->fcf_mac)) { fcoe_ctlr_link_down(&qedf->ctlr); qedf_wait_for_upload(qedf); fcoe_ctlr_link_up(&qedf->ctlr); } kfree_skb(skb); } else { /* Everything else is handled by libfcoe */ __skb_pull(skb, ETH_HLEN); fcoe_ctlr_recv(&qedf->ctlr, skb); } }