xref: /openbmc/linux/drivers/scsi/libfc/fc_frame.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
242e9a92fSRobert Love /*
342e9a92fSRobert Love  * Copyright(c) 2007 Intel Corporation. All rights reserved.
442e9a92fSRobert Love  *
542e9a92fSRobert Love  * Maintained at www.Open-FCoE.org
642e9a92fSRobert Love  */
742e9a92fSRobert Love 
842e9a92fSRobert Love /*
942e9a92fSRobert Love  * Frame allocation.
1042e9a92fSRobert Love  */
1142e9a92fSRobert Love #include <linux/module.h>
1242e9a92fSRobert Love #include <linux/kernel.h>
1342e9a92fSRobert Love #include <linux/skbuff.h>
1442e9a92fSRobert Love #include <linux/crc32.h>
155a0e3ad6STejun Heo #include <linux/gfp.h>
1642e9a92fSRobert Love 
1742e9a92fSRobert Love #include <scsi/fc_frame.h>
1842e9a92fSRobert Love 
1942e9a92fSRobert Love /*
2042e9a92fSRobert Love  * Check the CRC in a frame.
2142e9a92fSRobert Love  */
fc_frame_crc_check(struct fc_frame * fp)2242e9a92fSRobert Love u32 fc_frame_crc_check(struct fc_frame *fp)
2342e9a92fSRobert Love {
2442e9a92fSRobert Love 	u32 crc;
2542e9a92fSRobert Love 	u32 error;
2642e9a92fSRobert Love 	const u8 *bp;
2742e9a92fSRobert Love 	unsigned int len;
2842e9a92fSRobert Love 
2942e9a92fSRobert Love 	WARN_ON(!fc_frame_is_linear(fp));
3042e9a92fSRobert Love 	fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
3142e9a92fSRobert Love 	len = (fr_len(fp) + 3) & ~3;	/* round up length to include fill */
3242e9a92fSRobert Love 	bp = (const u8 *) fr_hdr(fp);
3342e9a92fSRobert Love 	crc = ~crc32(~0, bp, len);
3442e9a92fSRobert Love 	error = crc ^ fr_crc(fp);
3542e9a92fSRobert Love 	return error;
3642e9a92fSRobert Love }
3742e9a92fSRobert Love EXPORT_SYMBOL(fc_frame_crc_check);
3842e9a92fSRobert Love 
3942e9a92fSRobert Love /*
401bd49b48SVasu Dev  * Allocate a frame intended to be sent.
4142e9a92fSRobert Love  * Get an sk_buff for the frame and set the length.
4242e9a92fSRobert Love  */
_fc_frame_alloc(size_t len)43a7bbc7f4SVasu Dev struct fc_frame *_fc_frame_alloc(size_t len)
4442e9a92fSRobert Love {
4542e9a92fSRobert Love 	struct fc_frame *fp;
4642e9a92fSRobert Love 	struct sk_buff *skb;
4742e9a92fSRobert Love 
4842e9a92fSRobert Love 	WARN_ON((len % sizeof(u32)) != 0);
4942e9a92fSRobert Love 	len += sizeof(struct fc_frame_header);
5018fa11efSChris Leech 	skb = alloc_skb_fclone(len + FC_FRAME_HEADROOM + FC_FRAME_TAILROOM +
5118fa11efSChris Leech 			       NET_SKB_PAD, GFP_ATOMIC);
5242e9a92fSRobert Love 	if (!skb)
5342e9a92fSRobert Love 		return NULL;
5418fa11efSChris Leech 	skb_reserve(skb, NET_SKB_PAD + FC_FRAME_HEADROOM);
5542e9a92fSRobert Love 	fp = (struct fc_frame *) skb;
5642e9a92fSRobert Love 	fc_frame_init(fp);
5742e9a92fSRobert Love 	skb_put(skb, len);
5842e9a92fSRobert Love 	return fp;
5942e9a92fSRobert Love }
60a7bbc7f4SVasu Dev EXPORT_SYMBOL(_fc_frame_alloc);
6142e9a92fSRobert Love 
fc_frame_alloc_fill(struct fc_lport * lp,size_t payload_len)6242e9a92fSRobert Love struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
6342e9a92fSRobert Love {
6442e9a92fSRobert Love 	struct fc_frame *fp;
6542e9a92fSRobert Love 	size_t fill;
6642e9a92fSRobert Love 
6742e9a92fSRobert Love 	fill = payload_len % 4;
6842e9a92fSRobert Love 	if (fill != 0)
6942e9a92fSRobert Love 		fill = 4 - fill;
70a7bbc7f4SVasu Dev 	fp = _fc_frame_alloc(payload_len + fill);
7142e9a92fSRobert Love 	if (fp) {
7242e9a92fSRobert Love 		memset((char *) fr_hdr(fp) + payload_len, 0, fill);
7342e9a92fSRobert Love 		/* trim is OK, we just allocated it so there are no fragments */
7442e9a92fSRobert Love 		skb_trim(fp_skb(fp),
7542e9a92fSRobert Love 			 payload_len + sizeof(struct fc_frame_header));
7642e9a92fSRobert Love 	}
7742e9a92fSRobert Love 	return fp;
7842e9a92fSRobert Love }
79dc8596d3SChris Leech EXPORT_SYMBOL(fc_frame_alloc_fill);
80