diff options
Diffstat (limited to 'drivers/net/can/rx-offload.c')
-rw-r--r-- | drivers/net/can/rx-offload.c | 140 |
1 files changed, 89 insertions, 51 deletions
diff --git a/drivers/net/can/rx-offload.c b/drivers/net/can/rx-offload.c index e6a668ee7730..e8328910a234 100644 --- a/drivers/net/can/rx-offload.c +++ b/drivers/net/can/rx-offload.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2014 David Jander, Protonic Holland - * Copyright (C) 2014-2017 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de> +/* Copyright (c) 2014 Protonic Holland, + * David Jander + * Copyright (C) 2014-2017 Pengutronix, + * Marc Kleine-Budde <kernel@pengutronix.de> */ #include <linux/can/dev.h> @@ -11,14 +12,17 @@ struct can_rx_offload_cb { u32 timestamp; }; -static inline struct can_rx_offload_cb *can_rx_offload_get_cb(struct sk_buff *skb) +static inline struct can_rx_offload_cb * +can_rx_offload_get_cb(struct sk_buff *skb) { BUILD_BUG_ON(sizeof(struct can_rx_offload_cb) > sizeof(skb->cb)); return (struct can_rx_offload_cb *)skb->cb; } -static inline bool can_rx_offload_le(struct can_rx_offload *offload, unsigned int a, unsigned int b) +static inline bool +can_rx_offload_le(struct can_rx_offload *offload, + unsigned int a, unsigned int b) { if (offload->inc) return a <= b; @@ -26,7 +30,8 @@ static inline bool can_rx_offload_le(struct can_rx_offload *offload, unsigned in return a >= b; } -static inline unsigned int can_rx_offload_inc(struct can_rx_offload *offload, unsigned int *val) +static inline unsigned int +can_rx_offload_inc(struct can_rx_offload *offload, unsigned int *val) { if (offload->inc) return (*val)++; @@ -36,7 +41,9 @@ static inline unsigned int can_rx_offload_inc(struct can_rx_offload *offload, un static int can_rx_offload_napi_poll(struct napi_struct *napi, int quota) { - struct can_rx_offload *offload = container_of(napi, struct can_rx_offload, napi); + struct can_rx_offload *offload = container_of(napi, + struct can_rx_offload, + napi); struct net_device *dev = offload->dev; struct net_device_stats *stats = &dev->stats; struct sk_buff *skb; @@ -65,8 +72,9 @@ static int can_rx_offload_napi_poll(struct napi_struct *napi, int quota) return work_done; } -static inline void __skb_queue_add_sort(struct sk_buff_head *head, struct sk_buff *new, - int (*compare)(struct sk_buff *a, struct sk_buff *b)) +static inline void +__skb_queue_add_sort(struct sk_buff_head *head, struct sk_buff *new, + int (*compare)(struct sk_buff *a, struct sk_buff *b)) { struct sk_buff *pos, *insert = NULL; @@ -101,47 +109,70 @@ static int can_rx_offload_compare(struct sk_buff *a, struct sk_buff *b) cb_a = can_rx_offload_get_cb(a); cb_b = can_rx_offload_get_cb(b); - /* Substract two u32 and return result as int, to keep + /* Subtract two u32 and return result as int, to keep * difference steady around the u32 overflow. */ return cb_b->timestamp - cb_a->timestamp; } -static struct sk_buff *can_rx_offload_offload_one(struct can_rx_offload *offload, unsigned int n) +/** + * can_rx_offload_offload_one() - Read one CAN frame from HW + * @offload: pointer to rx_offload context + * @n: number of mailbox to read + * + * The task of this function is to read a CAN frame from mailbox @n + * from the device and return the mailbox's content as a struct + * sk_buff. + * + * If the struct can_rx_offload::skb_queue exceeds the maximal queue + * length (struct can_rx_offload::skb_queue_len_max) or no skb can be + * allocated, the mailbox contents is discarded by reading it into an + * overflow buffer. This way the mailbox is marked as free by the + * driver. + * + * Return: A pointer to skb containing the CAN frame on success. + * + * NULL if the mailbox @n is empty. + * + * ERR_PTR() in case of an error + */ +static struct sk_buff * +can_rx_offload_offload_one(struct can_rx_offload *offload, unsigned int n) { - struct sk_buff *skb = NULL; + struct sk_buff *skb; struct can_rx_offload_cb *cb; - struct can_frame *cf; - int ret; + bool drop = false; + u32 timestamp; - /* If queue is full or skb not available, read to discard mailbox */ - if (likely(skb_queue_len(&offload->skb_queue) <= - offload->skb_queue_len_max)) - skb = alloc_can_skb(offload->dev, &cf); + /* If queue is full drop frame */ + if (unlikely(skb_queue_len(&offload->skb_queue) > + offload->skb_queue_len_max)) + drop = true; - if (!skb) { - struct can_frame cf_overflow; - u32 timestamp; + skb = offload->mailbox_read(offload, n, ×tamp, drop); + /* Mailbox was empty. */ + if (unlikely(!skb)) + return NULL; - ret = offload->mailbox_read(offload, &cf_overflow, - ×tamp, n); - if (ret) - offload->dev->stats.rx_dropped++; + /* There was a problem reading the mailbox, propagate + * error value. + */ + if (unlikely(IS_ERR(skb))) { + offload->dev->stats.rx_dropped++; + offload->dev->stats.rx_fifo_errors++; - return NULL; + return skb; } + /* Mailbox was read. */ cb = can_rx_offload_get_cb(skb); - ret = offload->mailbox_read(offload, cf, &cb->timestamp, n); - if (!ret) { - kfree_skb(skb); - return NULL; - } + cb->timestamp = timestamp; return skb; } -int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload, u64 pending) +int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload, + u64 pending) { struct sk_buff_head skb_queue; unsigned int i; @@ -157,8 +188,8 @@ int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload, u64 pen continue; skb = can_rx_offload_offload_one(offload, i); - if (!skb) - break; + if (IS_ERR_OR_NULL(skb)) + continue; __skb_queue_add_sort(&skb_queue, skb, can_rx_offload_compare); } @@ -171,8 +202,8 @@ int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload, u64 pen skb_queue_splice_tail(&skb_queue, &offload->skb_queue); spin_unlock_irqrestore(&offload->skb_queue.lock, flags); - if ((queue_len = skb_queue_len(&offload->skb_queue)) > - (offload->skb_queue_len_max / 8)) + queue_len = skb_queue_len(&offload->skb_queue); + if (queue_len > offload->skb_queue_len_max / 8) netdev_dbg(offload->dev, "%s: queue_len=%d\n", __func__, queue_len); @@ -188,7 +219,13 @@ int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload) struct sk_buff *skb; int received = 0; - while ((skb = can_rx_offload_offload_one(offload, 0))) { + while (1) { + skb = can_rx_offload_offload_one(offload, 0); + if (IS_ERR(skb)) + continue; + if (!skb) + break; + skb_queue_tail(&offload->skb_queue, skb); received++; } @@ -207,8 +244,10 @@ int can_rx_offload_queue_sorted(struct can_rx_offload *offload, unsigned long flags; if (skb_queue_len(&offload->skb_queue) > - offload->skb_queue_len_max) - return -ENOMEM; + offload->skb_queue_len_max) { + kfree_skb(skb); + return -ENOBUFS; + } cb = can_rx_offload_get_cb(skb); cb->timestamp = timestamp; @@ -250,8 +289,10 @@ int can_rx_offload_queue_tail(struct can_rx_offload *offload, struct sk_buff *skb) { if (skb_queue_len(&offload->skb_queue) > - offload->skb_queue_len_max) - return -ENOMEM; + offload->skb_queue_len_max) { + kfree_skb(skb); + return -ENOBUFS; + } skb_queue_tail(&offload->skb_queue, skb); can_rx_offload_schedule(offload); @@ -260,7 +301,9 @@ int can_rx_offload_queue_tail(struct can_rx_offload *offload, } EXPORT_SYMBOL_GPL(can_rx_offload_queue_tail); -static int can_rx_offload_init_queue(struct net_device *dev, struct can_rx_offload *offload, unsigned int weight) +static int can_rx_offload_init_queue(struct net_device *dev, + struct can_rx_offload *offload, + unsigned int weight) { offload->dev = dev; @@ -269,7 +312,6 @@ static int can_rx_offload_init_queue(struct net_device *dev, struct can_rx_offlo offload->skb_queue_len_max *= 4; skb_queue_head_init(&offload->skb_queue); - can_rx_offload_reset(offload); netif_napi_add(dev, &offload->napi, can_rx_offload_napi_poll, weight); dev_dbg(dev->dev.parent, "%s: skb_queue_len_max=%d\n", @@ -278,7 +320,8 @@ static int can_rx_offload_init_queue(struct net_device *dev, struct can_rx_offlo return 0; } -int can_rx_offload_add_timestamp(struct net_device *dev, struct can_rx_offload *offload) +int can_rx_offload_add_timestamp(struct net_device *dev, + struct can_rx_offload *offload) { unsigned int weight; @@ -298,7 +341,8 @@ int can_rx_offload_add_timestamp(struct net_device *dev, struct can_rx_offload * } EXPORT_SYMBOL_GPL(can_rx_offload_add_timestamp); -int can_rx_offload_add_fifo(struct net_device *dev, struct can_rx_offload *offload, unsigned int weight) +int can_rx_offload_add_fifo(struct net_device *dev, + struct can_rx_offload *offload, unsigned int weight) { if (!offload->mailbox_read) return -EINVAL; @@ -309,7 +353,6 @@ EXPORT_SYMBOL_GPL(can_rx_offload_add_fifo); void can_rx_offload_enable(struct can_rx_offload *offload) { - can_rx_offload_reset(offload); napi_enable(&offload->napi); } EXPORT_SYMBOL_GPL(can_rx_offload_enable); @@ -320,8 +363,3 @@ void can_rx_offload_del(struct can_rx_offload *offload) skb_queue_purge(&offload->skb_queue); } EXPORT_SYMBOL_GPL(can_rx_offload_del); - -void can_rx_offload_reset(struct can_rx_offload *offload) -{ -} -EXPORT_SYMBOL_GPL(can_rx_offload_reset); |