| /* |
| * Copyright (c) 2014-2015 The Linux Foundation. All rights reserved. |
| * |
| * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| * |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /* |
| * This file was originally distributed by Qualcomm Atheros, Inc. |
| * under proprietary terms before Copyright ownership was assigned |
| * to the Linux Foundation. |
| */ |
| |
| /** |
| * DOC: i_cdf_nbuf.h |
| * |
| * Linux implementation of skbuf |
| */ |
| #ifndef _I_CDF_NET_BUF_H |
| #define _I_CDF_NET_BUF_H |
| |
| #include <linux/skbuff.h> |
| #include <linux/netdevice.h> |
| #include <linux/etherdevice.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/types.h> |
| #include <linux/scatterlist.h> |
| #include <cdf_types.h> |
| #include <cdf_status.h> |
| |
| #define __CDF_NBUF_NULL NULL |
| |
| |
| /* |
| * Use socket buffer as the underlying implentation as skbuf . |
| * Linux use sk_buff to represent both packet and data, |
| * so we use sk_buffer to represent both skbuf . |
| */ |
| typedef struct sk_buff *__cdf_nbuf_t; |
| |
| typedef void (*__cdf_nbuf_callback_fn)(struct sk_buff *skb); |
| #define OSDEP_EAPOL_TID 6 /* send it on VO queue */ |
| |
| /* CVG_NBUF_MAX_OS_FRAGS - |
| * max tx fragments provided by the OS |
| */ |
| #define CVG_NBUF_MAX_OS_FRAGS 1 |
| |
| /* CVG_NBUF_MAX_EXTRA_FRAGS - |
| * max tx fragments added by the driver |
| * The driver will always add one tx fragment (the tx descriptor) and may |
| * add a second tx fragment (e.g. a TSO segment's modified IP header). |
| */ |
| #define CVG_NBUF_MAX_EXTRA_FRAGS 2 |
| |
| typedef void (*cdf_nbuf_trace_update_t)(char *); |
| |
| /** |
| * struct cvg_nbuf_cb - network buffer control block |
| * @data_attr: Value that is programmed in CE descriptor, contains: |
| * 1) CE classification enablement bit |
| * 2) Pkt type (802.3 or Ethernet Type II) |
| * 3) Pkt Offset (Usually the length of HTT/HTC desc.) |
| * @trace: info for DP tracing |
| * @mapped_paddr_lo: DMA mapping info |
| * @extra_frags: Extra tx fragments |
| * @owner_id: Owner id |
| * @cdf_nbuf_callback_fn: Callback function |
| * @priv_data: IPA specific priv data |
| * @proto_type: Protocol type |
| * @vdev_id: vdev id |
| * @tx_htt2_frm: HTT 2 frame |
| * @tx_htt2_reserved: HTT 2 reserved bits |
| */ |
| struct cvg_nbuf_cb { |
| uint32_t data_attr; |
| /* |
| * Store info for data path tracing |
| */ |
| struct { |
| uint8_t packet_state; |
| uint8_t packet_track; |
| uint8_t dp_trace; |
| } trace; |
| |
| /* |
| * Store the DMA mapping info for the network buffer fragments |
| * provided by the OS. |
| */ |
| uint32_t mapped_paddr_lo[CVG_NBUF_MAX_OS_FRAGS]; |
| |
| /* store extra tx fragments provided by the driver */ |
| struct { |
| /* vaddr - |
| * CPU address (a.k.a. virtual address) of the tx fragments |
| * added by the driver |
| */ |
| unsigned char *vaddr[CVG_NBUF_MAX_EXTRA_FRAGS]; |
| /* paddr_lo - |
| * bus address (a.k.a. physical address) of the tx fragments |
| * added by the driver |
| */ |
| uint32_t paddr_lo[CVG_NBUF_MAX_EXTRA_FRAGS]; |
| uint16_t len[CVG_NBUF_MAX_EXTRA_FRAGS]; |
| uint8_t num; /* how many extra frags has the driver added */ |
| uint8_t |
| /* |
| * Store a wordstream vs. bytestream flag for each extra |
| * fragment, plus one more flag for the original fragment(s) |
| * of the netbuf. |
| */ |
| wordstream_flags:CVG_NBUF_MAX_EXTRA_FRAGS + 1; |
| } extra_frags; |
| uint32_t owner_id; |
| __cdf_nbuf_callback_fn cdf_nbuf_callback_fn; |
| unsigned long priv_data; |
| #ifdef QCA_PKT_PROTO_TRACE |
| unsigned char proto_type; |
| unsigned char vdev_id; |
| #endif /* QCA_PKT_PROTO_TRACE */ |
| #ifdef QCA_TX_HTT2_SUPPORT |
| unsigned char tx_htt2_frm:1; |
| unsigned char tx_htt2_reserved:7; |
| #endif /* QCA_TX_HTT2_SUPPORT */ |
| }; |
| #define NBUF_OWNER_ID(skb) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->owner_id) |
| #define NBUF_OWNER_PRIV_DATA(skb) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->priv_data) |
| #define NBUF_CALLBACK_FN(skb) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->cdf_nbuf_callback_fn) |
| #define NBUF_CALLBACK_FN_EXEC(skb) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->cdf_nbuf_callback_fn)(skb) |
| #define NBUF_MAPPED_PADDR_LO(skb) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->mapped_paddr_lo[0]) |
| #define NBUF_NUM_EXTRA_FRAGS(skb) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->extra_frags.num) |
| #define NBUF_EXTRA_FRAG_VADDR(skb, frag_num) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->extra_frags.vaddr[(frag_num)]) |
| #define NBUF_EXTRA_FRAG_PADDR_LO(skb, frag_num) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->extra_frags.paddr_lo[(frag_num)]) |
| #define NBUF_EXTRA_FRAG_LEN(skb, frag_num) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->extra_frags.len[(frag_num)]) |
| #define NBUF_EXTRA_FRAG_WORDSTREAM_FLAGS(skb) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->extra_frags.wordstream_flags) |
| |
| #ifdef QCA_PKT_PROTO_TRACE |
| #define NBUF_SET_PROTO_TYPE(skb, proto_type) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->proto_type = proto_type) |
| #define NBUF_GET_PROTO_TYPE(skb) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->proto_type) |
| #else |
| #define NBUF_SET_PROTO_TYPE(skb, proto_type); |
| #define NBUF_GET_PROTO_TYPE(skb) 0; |
| #endif /* QCA_PKT_PROTO_TRACE */ |
| |
| #ifdef QCA_TX_HTT2_SUPPORT |
| #define NBUF_SET_TX_HTT2_FRM(skb, candi) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->tx_htt2_frm = candi) |
| #define NBUF_GET_TX_HTT2_FRM(skb) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->tx_htt2_frm) |
| #else |
| #define NBUF_SET_TX_HTT2_FRM(skb, candi) |
| #define NBUF_GET_TX_HTT2_FRM(skb) 0 |
| #endif /* QCA_TX_HTT2_SUPPORT */ |
| |
| #define NBUF_DATA_ATTR_SET(skb, data_attr) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->data_attr = data_attr) |
| |
| #define NBUF_DATA_ATTR_GET(skb) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->data_attr) |
| |
| #if defined(FEATURE_LRO) |
| /** |
| * struct nbuf_rx_cb - network buffer control block |
| * on the receive path of the skb |
| * @lro_eligible: indicates whether the msdu is LRO eligible |
| * @tcp_proto: indicates if this is a TCP packet |
| * @ipv6_proto: indicates if this is an IPv6 packet |
| * @ip_offset: offset to the IP header |
| * @tcp_offset: offset to the TCP header |
| * @tcp_udp_chksum: TCP payload checksum |
| * @tcp_seq_num: TCP sequence number |
| * @tcp_ack_num: TCP acknowledgement number |
| * @flow_id_toeplitz: 32 bit 5-tuple flow id toeplitz hash |
| */ |
| struct nbuf_rx_cb { |
| uint32_t lro_eligible:1, |
| tcp_proto:1, |
| tcp_pure_ack:1, |
| ipv6_proto:1, |
| ip_offset:7, |
| tcp_offset:7; |
| uint32_t tcp_udp_chksum:16, |
| tcp_win:16; |
| uint32_t tcp_seq_num; |
| uint32_t tcp_ack_num; |
| uint32_t flow_id_toeplitz; |
| }; |
| |
| #define NBUF_LRO_ELIGIBLE(skb) \ |
| (((struct nbuf_rx_cb *)((skb)->cb))->lro_eligible) |
| #define NBUF_TCP_PROTO(skb) \ |
| (((struct nbuf_rx_cb *)((skb)->cb))->tcp_proto) |
| #define NBUF_TCP_PURE_ACK(skb) \ |
| (((struct nbuf_rx_cb *)((skb)->cb))->tcp_pure_ack) |
| #define NBUF_IPV6_PROTO(skb) \ |
| (((struct nbuf_rx_cb *)((skb)->cb))->ipv6_proto) |
| #define NBUF_IP_OFFSET(skb) \ |
| (((struct nbuf_rx_cb *)((skb)->cb))->ip_offset) |
| #define NBUF_TCP_OFFSET(skb) \ |
| (((struct nbuf_rx_cb *)((skb)->cb))->tcp_offset) |
| #define NBUF_TCP_CHKSUM(skb) \ |
| (((struct nbuf_rx_cb *)((skb)->cb))->tcp_udp_chksum) |
| #define NBUF_TCP_SEQ_NUM(skb) \ |
| (((struct nbuf_rx_cb *)((skb)->cb))->tcp_seq_num) |
| #define NBUF_TCP_ACK_NUM(skb) \ |
| (((struct nbuf_rx_cb *)((skb)->cb))->tcp_ack_num) |
| #define NBUF_TCP_WIN(skb) \ |
| (((struct nbuf_rx_cb *)((skb)->cb))->tcp_win) |
| #define NBUF_FLOW_ID_TOEPLITZ(skb) \ |
| (((struct nbuf_rx_cb *)((skb)->cb))->flow_id_toeplitz) |
| |
| /** |
| * cdf_print_lro_info() - prints the LRO information |
| * @skb : network buffer |
| * |
| * This function prints out the LRO related fields in the rx |
| * descriptor |
| * |
| * Return: none |
| */ |
| static inline void cdf_print_lro_info(struct sk_buff *skb) |
| { |
| cdf_print("NBUF_LRO_ELIGIBLE 0x%x\n" |
| "NBUF_TCP_PROTO 0x%x\n" |
| "NBUF_TCP_PURE_ACK 0x%x\n" |
| "NBUF_TCP_CHKSUM 0x%x\n" |
| "NBUF_IPV6_PROTO 0x%x\n" |
| "NBUF_IP_OFFSET 0x%x\n" |
| "NBUF_TCP_OFFSET 0x%x\n" |
| "NBUF_TCP_SEQ_NUM 0x%x\n" |
| "NBUF_TCP_ACK_NUM 0x%x\n" |
| "NBUF_TCP_WIN 0x%x\n" |
| "NBUF_FLOW_ID_TOEPLITZ 0x%x\n", |
| NBUF_LRO_ELIGIBLE(skb), |
| NBUF_TCP_PROTO(skb), |
| NBUF_TCP_PURE_ACK(skb), |
| NBUF_TCP_CHKSUM(skb), |
| NBUF_IPV6_PROTO(skb), |
| NBUF_IP_OFFSET(skb), |
| NBUF_TCP_OFFSET(skb), |
| NBUF_TCP_SEQ_NUM(skb), |
| NBUF_TCP_ACK_NUM(skb), |
| NBUF_TCP_WIN(skb), |
| NBUF_FLOW_ID_TOEPLITZ(skb)); |
| } |
| #endif /* FEATURE_LRO */ |
| |
| #define NBUF_SET_PACKET_STATE(skb, pkt_state) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->trace.packet_state = \ |
| pkt_state) |
| #define NBUF_GET_PACKET_STATE(skb) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->trace.packet_state) |
| |
| #define NBUF_SET_PACKET_TRACK(skb, pkt_track) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->trace.packet_track = \ |
| pkt_track) |
| #define NBUF_GET_PACKET_TRACK(skb) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->trace.packet_track) |
| |
| #define NBUF_UPDATE_TX_PKT_COUNT(skb, PACKET_STATE) \ |
| cdf_nbuf_set_state(skb, PACKET_STATE) |
| |
| #define CDF_NBUF_SET_DP_TRACE(skb, enable) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->trace.dp_trace \ |
| = enable) |
| #define CDF_NBUF_GET_DP_TRACE(skb) \ |
| (((struct cvg_nbuf_cb *)((skb)->cb))->trace.dp_trace) |
| |
| #define __cdf_nbuf_get_num_frags(skb) \ |
| /* assume the OS provides a single fragment */ \ |
| (NBUF_NUM_EXTRA_FRAGS(skb) + 1) |
| |
| #if defined(FEATURE_TSO) |
| #define __cdf_nbuf_dec_num_frags(skb) \ |
| (NBUF_NUM_EXTRA_FRAGS(skb)--) |
| #endif |
| |
| #define __cdf_nbuf_frag_push_head( \ |
| skb, frag_len, frag_vaddr, frag_paddr_lo, frag_paddr_hi) \ |
| do { \ |
| int frag_num = NBUF_NUM_EXTRA_FRAGS(skb)++; \ |
| NBUF_EXTRA_FRAG_VADDR(skb, frag_num) = frag_vaddr; \ |
| NBUF_EXTRA_FRAG_PADDR_LO(skb, frag_num) = frag_paddr_lo; \ |
| NBUF_EXTRA_FRAG_LEN(skb, frag_num) = frag_len; \ |
| } while (0) |
| |
| #define __cdf_nbuf_get_frag_len(skb, frag_num) \ |
| ((frag_num < NBUF_NUM_EXTRA_FRAGS(skb)) ? \ |
| NBUF_EXTRA_FRAG_LEN(skb, frag_num) : (skb)->len) |
| |
| #define __cdf_nbuf_get_frag_vaddr(skb, frag_num) \ |
| ((frag_num < NBUF_NUM_EXTRA_FRAGS(skb)) ? \ |
| NBUF_EXTRA_FRAG_VADDR(skb, frag_num) : ((skb)->data)) |
| |
| #define __cdf_nbuf_get_frag_paddr_lo(skb, frag_num) \ |
| ((frag_num < NBUF_NUM_EXTRA_FRAGS(skb)) ? \ |
| NBUF_EXTRA_FRAG_PADDR_LO(skb, frag_num) : \ |
| /* assume that the OS only provides a single fragment */ \ |
| NBUF_MAPPED_PADDR_LO(skb)) |
| |
| #define __cdf_nbuf_get_frag_is_wordstream(skb, frag_num) \ |
| ((frag_num < NBUF_NUM_EXTRA_FRAGS(skb)) ? \ |
| (NBUF_EXTRA_FRAG_WORDSTREAM_FLAGS(skb) >> \ |
| (frag_num)) & 0x1 : \ |
| (NBUF_EXTRA_FRAG_WORDSTREAM_FLAGS(skb) >> \ |
| (CVG_NBUF_MAX_EXTRA_FRAGS)) & 0x1) |
| |
| #define __cdf_nbuf_set_frag_is_wordstream(skb, frag_num, is_wordstream) \ |
| do { \ |
| if (frag_num >= NBUF_NUM_EXTRA_FRAGS(skb)) { \ |
| frag_num = CVG_NBUF_MAX_EXTRA_FRAGS; \ |
| } \ |
| /* clear the old value */ \ |
| NBUF_EXTRA_FRAG_WORDSTREAM_FLAGS(skb) &= ~(1 << frag_num); \ |
| /* set the new value */ \ |
| NBUF_EXTRA_FRAG_WORDSTREAM_FLAGS(skb) |= \ |
| ((is_wordstream) << frag_num); \ |
| } while (0) |
| |
| #define __cdf_nbuf_trace_set_proto_type(skb, proto_type) \ |
| NBUF_SET_PROTO_TYPE(skb, proto_type) |
| #define __cdf_nbuf_trace_get_proto_type(skb) \ |
| NBUF_GET_PROTO_TYPE(skb); |
| |
| /** |
| * __cdf_nbuf_data_attr_get() - Retrieves the data_attr value |
| * from cvg_nbuf_cb (skb->cb) |
| * @skb: Pointer to struct sk_buff |
| * |
| * Return: data_attr |
| */ |
| #define __cdf_nbuf_data_attr_get(skb) \ |
| NBUF_DATA_ATTR_GET(skb) |
| |
| /** |
| * __cdf_nbuf_data_attr_set() - Sets the data_attr value |
| * in cvg_nbuf_cb (skb->cb) |
| * @skb: Pointer to struct sk_buff |
| * @data_attr: packet type from the enum cdf_txrx_pkt_type |
| * |
| * Return: |
| */ |
| static inline void |
| __cdf_nbuf_data_attr_set(struct sk_buff *skb, |
| uint32_t data_attr) |
| { |
| NBUF_DATA_ATTR_SET(skb, data_attr); |
| } |
| |
| /** |
| * typedef struct __cdf_nbuf_queue_t - network buffer queue |
| * @head: Head pointer |
| * @tail: Tail pointer |
| * @qlen: Queue length |
| */ |
| typedef struct __cdf_nbuf_qhead { |
| struct sk_buff *head; |
| struct sk_buff *tail; |
| unsigned int qlen; |
| } __cdf_nbuf_queue_t; |
| |
| /* |
| * Use sk_buff_head as the implementation of cdf_nbuf_queue_t. |
| * Because the queue head will most likely put in some structure, |
| * we don't use pointer type as the definition. |
| */ |
| |
| /* |
| * prototypes. Implemented in cdf_nbuf.c |
| */ |
| __cdf_nbuf_t __cdf_nbuf_alloc(__cdf_device_t osdev, size_t size, int reserve, |
| int align, int prio); |
| void __cdf_nbuf_free(struct sk_buff *skb); |
| CDF_STATUS __cdf_nbuf_map(__cdf_device_t osdev, |
| struct sk_buff *skb, cdf_dma_dir_t dir); |
| void __cdf_nbuf_unmap(__cdf_device_t osdev, |
| struct sk_buff *skb, cdf_dma_dir_t dir); |
| CDF_STATUS __cdf_nbuf_map_single(__cdf_device_t osdev, |
| struct sk_buff *skb, cdf_dma_dir_t dir); |
| void __cdf_nbuf_unmap_single(__cdf_device_t osdev, |
| struct sk_buff *skb, cdf_dma_dir_t dir); |
| void __cdf_nbuf_reg_trace_cb(cdf_nbuf_trace_update_t cb_func_ptr); |
| |
| #ifdef QCA_PKT_PROTO_TRACE |
| void __cdf_nbuf_trace_update(struct sk_buff *buf, char *event_string); |
| #else |
| #define __cdf_nbuf_trace_update(skb, event_string) |
| #endif /* QCA_PKT_PROTO_TRACE */ |
| |
| /** |
| * __cdf_os_to_status() - OS to CDF status conversion |
| * @error : OS error |
| * |
| * Return: CDF status |
| */ |
| static inline CDF_STATUS __cdf_os_to_status(signed int error) |
| { |
| switch (error) { |
| case 0: |
| return CDF_STATUS_SUCCESS; |
| case ENOMEM: |
| case -ENOMEM: |
| return CDF_STATUS_E_NOMEM; |
| default: |
| return CDF_STATUS_E_NOSUPPORT; |
| } |
| } |
| |
| /** |
| * __cdf_nbuf_len() - return the amount of valid data in the skb |
| * @skb: Pointer to network buffer |
| * |
| * This API returns the amount of valid data in the skb, If there are frags |
| * then it returns total length. |
| * |
| * Return: network buffer length |
| */ |
| static inline size_t __cdf_nbuf_len(struct sk_buff *skb) |
| { |
| int i, extra_frag_len = 0; |
| |
| i = NBUF_NUM_EXTRA_FRAGS(skb); |
| while (i-- > 0) |
| extra_frag_len += NBUF_EXTRA_FRAG_LEN(skb, i); |
| |
| return extra_frag_len + skb->len; |
| } |
| |
| /** |
| * __cdf_nbuf_cat() - link two nbufs |
| * @dst: Buffer to piggyback into |
| * @src: Buffer to put |
| * |
| * Link tow nbufs the new buf is piggybacked into the older one. The older |
| * (src) skb is released. |
| * |
| * Return: CDF_STATUS (status of the call) if failed the src skb |
| * is released |
| */ |
| static inline CDF_STATUS |
| __cdf_nbuf_cat(struct sk_buff *dst, struct sk_buff *src) |
| { |
| CDF_STATUS error = 0; |
| |
| cdf_assert(dst && src); |
| |
| /* |
| * Since pskb_expand_head unconditionally reallocates the skb->head |
| * buffer, first check whether the current buffer is already large |
| * enough. |
| */ |
| if (skb_tailroom(dst) < src->len) { |
| error = pskb_expand_head(dst, 0, src->len, GFP_ATOMIC); |
| if (error) |
| return __cdf_os_to_status(error); |
| } |
| memcpy(skb_tail_pointer(dst), src->data, src->len); |
| |
| skb_put(dst, src->len); |
| dev_kfree_skb_any(src); |
| |
| return __cdf_os_to_status(error); |
| } |
| |
| /**************************nbuf manipulation routines*****************/ |
| |
| /** |
| * __cdf_nbuf_headroom() - return the amount of tail space available |
| * @buf: Pointer to network buffer |
| * |
| * Return: amount of tail room |
| */ |
| static inline int __cdf_nbuf_headroom(struct sk_buff *skb) |
| { |
| return skb_headroom(skb); |
| } |
| |
| /** |
| * __cdf_nbuf_tailroom() - return the amount of tail space available |
| * @buf: Pointer to network buffer |
| * |
| * Return: amount of tail room |
| */ |
| static inline uint32_t __cdf_nbuf_tailroom(struct sk_buff *skb) |
| { |
| return skb_tailroom(skb); |
| } |
| |
| /** |
| * __cdf_nbuf_push_head() - Push data in the front |
| * @skb: Pointer to network buffer |
| * @size: size to be pushed |
| * |
| * Return: New data pointer of this buf after data has been pushed, |
| * or NULL if there is not enough room in this buf. |
| */ |
| static inline uint8_t *__cdf_nbuf_push_head(struct sk_buff *skb, size_t size) |
| { |
| if (NBUF_MAPPED_PADDR_LO(skb)) |
| NBUF_MAPPED_PADDR_LO(skb) -= size; |
| |
| return skb_push(skb, size); |
| } |
| |
| /** |
| * __cdf_nbuf_put_tail() - Puts data in the end |
| * @skb: Pointer to network buffer |
| * @size: size to be pushed |
| * |
| * Return: data pointer of this buf where new data has to be |
| * put, or NULL if there is not enough room in this buf. |
| */ |
| static inline uint8_t *__cdf_nbuf_put_tail(struct sk_buff *skb, size_t size) |
| { |
| if (skb_tailroom(skb) < size) { |
| if (unlikely(pskb_expand_head(skb, 0, |
| size - skb_tailroom(skb), GFP_ATOMIC))) { |
| dev_kfree_skb_any(skb); |
| return NULL; |
| } |
| } |
| return skb_put(skb, size); |
| } |
| |
| /** |
| * __cdf_nbuf_pull_head() - pull data out from the front |
| * @skb: Pointer to network buffer |
| * @size: size to be popped |
| * |
| * Return: New data pointer of this buf after data has been popped, |
| * or NULL if there is not sufficient data to pull. |
| */ |
| static inline uint8_t *__cdf_nbuf_pull_head(struct sk_buff *skb, size_t size) |
| { |
| if (NBUF_MAPPED_PADDR_LO(skb)) |
| NBUF_MAPPED_PADDR_LO(skb) += size; |
| |
| return skb_pull(skb, size); |
| } |
| |
| /** |
| * __cdf_nbuf_trim_tail() - trim data out from the end |
| * @skb: Pointer to network buffer |
| * @size: size to be popped |
| * |
| * Return: none |
| */ |
| static inline void __cdf_nbuf_trim_tail(struct sk_buff *skb, size_t size) |
| { |
| return skb_trim(skb, skb->len - size); |
| } |
| |
| /*********************nbuf private buffer routines*************/ |
| |
| /** |
| * __cdf_nbuf_peek_header() - return the header's addr & m_len |
| * @skb: Pointer to network buffer |
| * @addr: Pointer to store header's addr |
| * @m_len: network buffer length |
| * |
| * Return: none |
| */ |
| static inline void |
| __cdf_nbuf_peek_header(struct sk_buff *skb, uint8_t **addr, uint32_t *len) |
| { |
| *addr = skb->data; |
| *len = skb->len; |
| } |
| |
| /******************Custom queue*************/ |
| |
| /** |
| * __cdf_nbuf_queue_init() - initiallize the queue head |
| * @qhead: Queue head |
| * |
| * Return: CDF status |
| */ |
| static inline CDF_STATUS __cdf_nbuf_queue_init(__cdf_nbuf_queue_t *qhead) |
| { |
| memset(qhead, 0, sizeof(struct __cdf_nbuf_qhead)); |
| return CDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * __cdf_nbuf_queue_add() - add an skb in the tail of the queue |
| * @qhead: Queue head |
| * @skb: Pointer to network buffer |
| * |
| * This is a lockless version, driver must acquire locks if it |
| * needs to synchronize |
| * |
| * Return: none |
| */ |
| static inline void |
| __cdf_nbuf_queue_add(__cdf_nbuf_queue_t *qhead, struct sk_buff *skb) |
| { |
| skb->next = NULL; /*Nullify the next ptr */ |
| |
| if (!qhead->head) |
| qhead->head = skb; |
| else |
| qhead->tail->next = skb; |
| |
| qhead->tail = skb; |
| qhead->qlen++; |
| } |
| |
| /** |
| * __cdf_nbuf_queue_insert_head() - add an skb at the head of the queue |
| * @qhead: Queue head |
| * @skb: Pointer to network buffer |
| * |
| * This is a lockless version, driver must acquire locks if it needs to |
| * synchronize |
| * |
| * Return: none |
| */ |
| static inline void |
| __cdf_nbuf_queue_insert_head(__cdf_nbuf_queue_t *qhead, __cdf_nbuf_t skb) |
| { |
| if (!qhead->head) { |
| /*Empty queue Tail pointer Must be updated */ |
| qhead->tail = skb; |
| } |
| skb->next = qhead->head; |
| qhead->head = skb; |
| qhead->qlen++; |
| } |
| |
| /** |
| * __cdf_nbuf_queue_remove() - remove a skb from the head of the queue |
| * @qhead: Queue head |
| * |
| * This is a lockless version. Driver should take care of the locks |
| * |
| * Return: skb or NULL |
| */ |
| static inline |
| struct sk_buff *__cdf_nbuf_queue_remove(__cdf_nbuf_queue_t *qhead) |
| { |
| __cdf_nbuf_t tmp = NULL; |
| |
| if (qhead->head) { |
| qhead->qlen--; |
| tmp = qhead->head; |
| if (qhead->head == qhead->tail) { |
| qhead->head = NULL; |
| qhead->tail = NULL; |
| } else { |
| qhead->head = tmp->next; |
| } |
| tmp->next = NULL; |
| } |
| return tmp; |
| } |
| |
| /** |
| * __cdf_nbuf_queue_len() - return the queue length |
| * @qhead: Queue head |
| * |
| * Return: Queue length |
| */ |
| static inline uint32_t __cdf_nbuf_queue_len(__cdf_nbuf_queue_t *qhead) |
| { |
| return qhead->qlen; |
| } |
| |
| /** |
| * __cdf_nbuf_queue_next() - return the next skb from packet chain |
| * @skb: Pointer to network buffer |
| * |
| * This API returns the next skb from packet chain, remember the skb is |
| * still in the queue |
| * |
| * Return: NULL if no packets are there |
| */ |
| static inline struct sk_buff *__cdf_nbuf_queue_next(struct sk_buff *skb) |
| { |
| return skb->next; |
| } |
| |
| /** |
| * __cdf_nbuf_is_queue_empty() - check if the queue is empty or not |
| * @qhead: Queue head |
| * |
| * Return: true if length is 0 else false |
| */ |
| static inline bool __cdf_nbuf_is_queue_empty(__cdf_nbuf_queue_t *qhead) |
| { |
| return qhead->qlen == 0; |
| } |
| |
| /* |
| * Use sk_buff_head as the implementation of cdf_nbuf_queue_t. |
| * Because the queue head will most likely put in some structure, |
| * we don't use pointer type as the definition. |
| */ |
| |
| /* |
| * prototypes. Implemented in cdf_nbuf.c |
| */ |
| cdf_nbuf_tx_cksum_t __cdf_nbuf_get_tx_cksum(struct sk_buff *skb); |
| CDF_STATUS __cdf_nbuf_set_rx_cksum(struct sk_buff *skb, |
| cdf_nbuf_rx_cksum_t *cksum); |
| uint8_t __cdf_nbuf_get_tid(struct sk_buff *skb); |
| void __cdf_nbuf_set_tid(struct sk_buff *skb, uint8_t tid); |
| uint8_t __cdf_nbuf_get_exemption_type(struct sk_buff *skb); |
| |
| /* |
| * cdf_nbuf_pool_delete() implementation - do nothing in linux |
| */ |
| #define __cdf_nbuf_pool_delete(osdev) |
| |
| /** |
| * __cdf_nbuf_clone() - clone the nbuf (copy is readonly) |
| * @skb: Pointer to network buffer |
| * |
| * if GFP_ATOMIC is overkill then we can check whether its |
| * called from interrupt context and then do it or else in |
| * normal case use GFP_KERNEL |
| * |
| * example use "in_irq() || irqs_disabled()" |
| * |
| * Return: cloned skb |
| */ |
| static inline struct sk_buff *__cdf_nbuf_clone(struct sk_buff *skb) |
| { |
| return skb_clone(skb, GFP_ATOMIC); |
| } |
| |
| /** |
| * __cdf_nbuf_copy() - returns a private copy of the skb |
| * @skb: Pointer to network buffer |
| * |
| * This API returns a private copy of the skb, the skb returned is completely |
| * modifiable by callers |
| * |
| * Return: skb or NULL |
| */ |
| static inline struct sk_buff *__cdf_nbuf_copy(struct sk_buff *skb) |
| { |
| return skb_copy(skb, GFP_ATOMIC); |
| } |
| |
| #define __cdf_nbuf_reserve skb_reserve |
| |
| /***********************XXX: misc api's************************/ |
| |
| /** |
| * __cdf_nbuf_head() - return the pointer the skb's head pointer |
| * @skb: Pointer to network buffer |
| * |
| * Return: Pointer to head buffer |
| */ |
| static inline uint8_t *__cdf_nbuf_head(struct sk_buff *skb) |
| { |
| return skb->head; |
| } |
| |
| /** |
| * __cdf_nbuf_data() - return the pointer to data header in the skb |
| * @skb: Pointer to network buffer |
| * |
| * Return: Pointer to skb data |
| */ |
| static inline uint8_t *__cdf_nbuf_data(struct sk_buff *skb) |
| { |
| return skb->data; |
| } |
| |
| /** |
| * __cdf_nbuf_get_protocol() - return the protocol value of the skb |
| * @skb: Pointer to network buffer |
| * |
| * Return: skb protocol |
| */ |
| static inline uint16_t __cdf_nbuf_get_protocol(struct sk_buff *skb) |
| { |
| return skb->protocol; |
| } |
| |
| /** |
| * __cdf_nbuf_get_ip_summed() - return the ip checksum value of the skb |
| * @skb: Pointer to network buffer |
| * |
| * Return: skb ip_summed |
| */ |
| static inline uint8_t __cdf_nbuf_get_ip_summed(struct sk_buff *skb) |
| { |
| return skb->ip_summed; |
| } |
| |
| /** |
| * __cdf_nbuf_set_ip_summed() - sets the ip_summed value of the skb |
| * @skb: Pointer to network buffer |
| * @ip_summed: ip checksum |
| * |
| * Return: none |
| */ |
| static inline void __cdf_nbuf_set_ip_summed(struct sk_buff *skb, uint8_t ip_summed) |
| { |
| skb->ip_summed = ip_summed; |
| } |
| |
| /** |
| * __cdf_nbuf_get_priority() - return the priority value of the skb |
| * @skb: Pointer to network buffer |
| * |
| * Return: skb priority |
| */ |
| static inline uint32_t __cdf_nbuf_get_priority(struct sk_buff *skb) |
| { |
| return skb->priority; |
| } |
| |
| /** |
| * __cdf_nbuf_set_priority() - sets the priority value of the skb |
| * @skb: Pointer to network buffer |
| * @p: priority |
| * |
| * Return: none |
| */ |
| static inline void __cdf_nbuf_set_priority(struct sk_buff *skb, uint32_t p) |
| { |
| skb->priority = p; |
| } |
| |
| /** |
| * __cdf_nbuf_set_next() - sets the next skb pointer of the current skb |
| * @skb: Current skb |
| * @next_skb: Next skb |
| * |
| * Return: void |
| */ |
| static inline void |
| __cdf_nbuf_set_next(struct sk_buff *skb, struct sk_buff *skb_next) |
| { |
| skb->next = skb_next; |
| } |
| |
| /** |
| * __cdf_nbuf_next() - return the next skb pointer of the current skb |
| * @skb: Current skb |
| * |
| * Return: the next skb pointed to by the current skb |
| */ |
| static inline struct sk_buff *__cdf_nbuf_next(struct sk_buff *skb) |
| { |
| return skb->next; |
| } |
| |
| /** |
| * __cdf_nbuf_set_next_ext() - sets the next skb pointer of the current skb |
| * @skb: Current skb |
| * @next_skb: Next skb |
| * |
| * This fn is used to link up extensions to the head skb. Does not handle |
| * linking to the head |
| * |
| * Return: none |
| */ |
| static inline void |
| __cdf_nbuf_set_next_ext(struct sk_buff *skb, struct sk_buff *skb_next) |
| { |
| skb->next = skb_next; |
| } |
| |
| /** |
| * __cdf_nbuf_next_ext() - return the next skb pointer of the current skb |
| * @skb: Current skb |
| * |
| * Return: the next skb pointed to by the current skb |
| */ |
| static inline struct sk_buff *__cdf_nbuf_next_ext(struct sk_buff *skb) |
| { |
| return skb->next; |
| } |
| |
| /** |
| * __cdf_nbuf_append_ext_list() - link list of packet extensions to the head |
| * @skb_head: head_buf nbuf holding head segment (single) |
| * @ext_list: nbuf list holding linked extensions to the head |
| * @ext_len: Total length of all buffers in the extension list |
| * |
| * This function is used to link up a list of packet extensions (seg1, 2,* ...) |
| * to the nbuf holding the head segment (seg0) |
| * |
| * Return: none |
| */ |
| static inline void |
| __cdf_nbuf_append_ext_list(struct sk_buff *skb_head, |
| struct sk_buff *ext_list, size_t ext_len) |
| { |
| skb_shinfo(skb_head)->frag_list = ext_list; |
| skb_head->data_len = ext_len; |
| skb_head->len += skb_head->data_len; |
| } |
| |
| /** |
| * __cdf_nbuf_tx_free() - free skb list |
| * @skb: Pointer to network buffer |
| * @tx_err: TX error |
| * |
| * Return: none |
| */ |
| static inline void __cdf_nbuf_tx_free(struct sk_buff *bufs, int tx_err) |
| { |
| while (bufs) { |
| struct sk_buff *next = __cdf_nbuf_next(bufs); |
| __cdf_nbuf_free(bufs); |
| bufs = next; |
| } |
| } |
| |
| /** |
| * __cdf_nbuf_get_age() - return the checksum value of the skb |
| * @skb: Pointer to network buffer |
| * |
| * Return: checksum value |
| */ |
| static inline uint32_t __cdf_nbuf_get_age(struct sk_buff *skb) |
| { |
| return skb->csum; |
| } |
| |
| /** |
| * __cdf_nbuf_set_age() - sets the checksum value of the skb |
| * @skb: Pointer to network buffer |
| * @v: Value |
| * |
| * Return: none |
| */ |
| static inline void __cdf_nbuf_set_age(struct sk_buff *skb, uint32_t v) |
| { |
| skb->csum = v; |
| } |
| |
| /** |
| * __cdf_nbuf_adj_age() - adjusts the checksum/age value of the skb |
| * @skb: Pointer to network buffer |
| * @adj: Adjustment value |
| * |
| * Return: none |
| */ |
| static inline void __cdf_nbuf_adj_age(struct sk_buff *skb, uint32_t adj) |
| { |
| skb->csum -= adj; |
| } |
| |
| /** |
| * __cdf_nbuf_copy_bits() - return the length of the copy bits for skb |
| * @skb: Pointer to network buffer |
| * @offset: Offset value |
| * @len: Length |
| * @to: Destination pointer |
| * |
| * Return: length of the copy bits for skb |
| */ |
| static inline int32_t |
| __cdf_nbuf_copy_bits(struct sk_buff *skb, int32_t offset, int32_t len, void *to) |
| { |
| return skb_copy_bits(skb, offset, to, len); |
| } |
| |
| /** |
| * __cdf_nbuf_set_pktlen() - sets the length of the skb and adjust the tail |
| * @skb: Pointer to network buffer |
| * @len: Packet length |
| * |
| * Return: none |
| */ |
| static inline void __cdf_nbuf_set_pktlen(struct sk_buff *skb, uint32_t len) |
| { |
| if (skb->len > len) { |
| skb_trim(skb, len); |
| } else { |
| if (skb_tailroom(skb) < len - skb->len) { |
| if (unlikely(pskb_expand_head(skb, 0, |
| len - skb->len - skb_tailroom(skb), |
| GFP_ATOMIC))) { |
| dev_kfree_skb_any(skb); |
| cdf_assert(0); |
| } |
| } |
| skb_put(skb, (len - skb->len)); |
| } |
| } |
| |
| /** |
| * __cdf_nbuf_set_protocol() - sets the protocol value of the skb |
| * @skb: Pointer to network buffer |
| * @protocol: Protocol type |
| * |
| * Return: none |
| */ |
| static inline void |
| __cdf_nbuf_set_protocol(struct sk_buff *skb, uint16_t protocol) |
| { |
| skb->protocol = protocol; |
| } |
| |
| #define __cdf_nbuf_set_tx_htt2_frm(skb, candi) \ |
| NBUF_SET_TX_HTT2_FRM(skb, candi) |
| #define __cdf_nbuf_get_tx_htt2_frm(skb) \ |
| NBUF_GET_TX_HTT2_FRM(skb) |
| |
| #if defined(FEATURE_TSO) |
| uint32_t __cdf_nbuf_get_tso_info(cdf_device_t osdev, struct sk_buff *skb, |
| struct cdf_tso_info_t *tso_info); |
| |
| uint32_t __cdf_nbuf_get_tso_num_seg(struct sk_buff *skb); |
| |
| static inline uint8_t __cdf_nbuf_is_tso(struct sk_buff *skb) |
| { |
| return skb_is_gso(skb); |
| } |
| |
| struct sk_buff *__cdf_nbuf_inc_users(struct sk_buff *skb); |
| #endif /* TSO */ |
| |
| /** |
| * __cdf_nbuf_tx_info_get() - Modify pkt_type, set pkt_subtype, |
| * and get hw_classify by peeking |
| * into packet |
| * @nbuf: Network buffer (skb on Linux) |
| * @pkt_type: Pkt type (from enum htt_pkt_type) |
| * @pkt_subtype: Bit 4 of this field in HTT descriptor |
| * needs to be set in case of CE classification support |
| * Is set by this macro. |
| * @hw_classify: This is a flag which is set to indicate |
| * CE classification is enabled. |
| * Do not set this bit for VLAN packets |
| * OR for mcast / bcast frames. |
| * |
| * This macro parses the payload to figure out relevant Tx meta-data e.g. |
| * whether to enable tx_classify bit in CE. |
| * |
| * Overrides pkt_type only if required for 802.3 frames (original ethernet) |
| * If protocol is less than ETH_P_802_3_MIN (0x600), then |
| * it is the length and a 802.3 frame else it is Ethernet Type II |
| * (RFC 894). |
| * Bit 4 in pkt_subtype is the tx_classify bit |
| * |
| * Return: void |
| */ |
| #define __cdf_nbuf_tx_info_get(skb, pkt_type, \ |
| pkt_subtype, hw_classify) \ |
| do { \ |
| struct ethhdr *eh = (struct ethhdr *)skb->data; \ |
| uint16_t ether_type = ntohs(eh->h_proto); \ |
| bool is_mc_bc; \ |
| \ |
| is_mc_bc = is_broadcast_ether_addr((uint8_t *)eh) || \ |
| is_multicast_ether_addr((uint8_t *)eh); \ |
| \ |
| if (likely((ether_type != ETH_P_8021Q) && !is_mc_bc)) { \ |
| hw_classify = 1; \ |
| pkt_subtype = 0x01 << \ |
| HTT_TX_CLASSIFY_BIT_S; \ |
| } \ |
| \ |
| if (unlikely(ether_type < ETH_P_802_3_MIN)) \ |
| pkt_type = htt_pkt_type_ethernet; \ |
| \ |
| } while (0) |
| #endif /*_I_CDF_NET_BUF_H */ |