/*
 * 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;
}
struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
{
	struct fc_frame *fp;
	size_t fill;

	fill = payload_len % 4;
	if (fill != 0)
		fill = 4 - fill;
	fp = _fc_frame_alloc(payload_len + fill);
	if (fp) {
		memset((char *) fr_hdr(fp) + payload_len, 0, fill);
		/* trim is OK, we just allocated it so there are no fragments */
		skb_trim(fp_skb(fp),
			 payload_len + sizeof(struct fc_frame_header));
	}
	return fp;
}
Beispiel #3
0
static inline int fnic_handle_flogi_resp(struct fnic *fnic,
					 struct fc_frame *fp)
{
	u8 mac[ETH_ALEN] = FC_FCOE_FLOGI_MAC;
	struct ethhdr *eth_hdr;
	struct fc_frame_header *fh;
	int ret = 0;
	unsigned long flags;
	struct fc_frame *old_flogi_resp = NULL;

	fh = (struct fc_frame_header *)fr_hdr(fp);

	spin_lock_irqsave(&fnic->fnic_lock, flags);

	if (fnic->state == FNIC_IN_ETH_MODE) {

		/*
		 * Check if oxid matches on taking the lock. A new Flogi
		 * issued by libFC might have changed the fnic cached oxid
		 */
		if (fnic->flogi_oxid != ntohs(fh->fh_ox_id)) {
			FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
				     "Flogi response oxid not"
				     " matching cached oxid, dropping frame"
				     "\n");
			ret = -1;
			spin_unlock_irqrestore(&fnic->fnic_lock, flags);
			dev_kfree_skb_irq(fp_skb(fp));
			goto handle_flogi_resp_end;
		}

		/* Drop older cached flogi response frame, cache this frame */
		old_flogi_resp = fnic->flogi_resp;
		fnic->flogi_resp = fp;
		fnic->flogi_oxid = FC_XID_UNKNOWN;

		/*
		 * this frame is part of flogi get the src mac addr from this
		 * frame if the src mac is fcoui based then we mark the
		 * address mode flag to use fcoui base for dst mac addr
		 * otherwise we have to store the fcoe gateway addr
		 */
		eth_hdr = (struct ethhdr *)skb_mac_header(fp_skb(fp));
		memcpy(mac, eth_hdr->h_source, ETH_ALEN);

		if (ntoh24(mac) == FC_FCOE_OUI)
			fnic->fcoui_mode = 1;
		else {
			fnic->fcoui_mode = 0;
			memcpy(fnic->dest_addr, mac, ETH_ALEN);
		}

		/*
		 * Except for Flogi frame, all outbound frames from us have the
		 * Eth Src address as FC_FCOE_OUI"our_sid". Flogi frame uses
		 * the vnic MAC address as the Eth Src address
		 */
		fc_fcoe_set_mac(fnic->data_src_addr, fh->fh_d_id);

		/* We get our s_id from the d_id of the flogi resp frame */
		fnic->s_id = ntoh24(fh->fh_d_id);

		/* Change state to reflect transition from Eth to FC mode */
		fnic->state = FNIC_IN_ETH_TRANS_FC_MODE;

	} else {
		FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
			     "Unexpected fnic state %s while"
			     " processing flogi resp\n",
			     fnic_state_to_str(fnic->state));
		ret = -1;
		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
		dev_kfree_skb_irq(fp_skb(fp));
		goto handle_flogi_resp_end;
	}

	spin_unlock_irqrestore(&fnic->fnic_lock, flags);

	/* Drop older cached frame */
	if (old_flogi_resp)
		dev_kfree_skb_irq(fp_skb(old_flogi_resp));

	/*
	 * send flogi reg request to firmware, this will put the fnic in
	 * in FC mode
	 */
	ret = fnic_flogi_reg_handler(fnic);

	if (ret < 0) {
		int free_fp = 1;
		spin_lock_irqsave(&fnic->fnic_lock, flags);
		/*
		 * free the frame is some other thread is not
		 * pointing to it
		 */
		if (fnic->flogi_resp != fp)
			free_fp = 0;
		else
			fnic->flogi_resp = NULL;

		if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE)
			fnic->state = FNIC_IN_ETH_MODE;
		spin_unlock_irqrestore(&fnic->fnic_lock, flags);
		if (free_fp)
			dev_kfree_skb_irq(fp_skb(fp));
	}

 handle_flogi_resp_end:
	return ret;
}