Skip to content

Commit d254586

Browse files
yopemarckleinebudde
authored andcommitted
can: rx-offload: Add support for HW fifo based irq offloading
Some CAN controllers have a usable FIFO already but can still benefit from off-loading the CAN controller FIFO. The CAN frames of the FIFO are read and put into a skb queue during interrupt and then transmitted in a NAPI context. Signed-off-by: David Jander <[email protected]> Signed-off-by: Marc Kleine-Budde <[email protected]>
1 parent bd092ad commit d254586

File tree

3 files changed

+209
-1
lines changed

3 files changed

+209
-1
lines changed

drivers/net/can/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ obj-$(CONFIG_CAN_VCAN) += vcan.o
66
obj-$(CONFIG_CAN_SLCAN) += slcan.o
77

88
obj-$(CONFIG_CAN_DEV) += can-dev.o
9-
can-dev-y := dev.o
9+
can-dev-y += dev.o
10+
can-dev-y += rx-offload.o
1011

1112
can-dev-$(CONFIG_CAN_LEDS) += led.o
1213

drivers/net/can/rx-offload.c

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright (c) 2014 David Jander, Protonic Holland
3+
* Copyright (C) 2014-2017 Pengutronix, Marc Kleine-Budde <[email protected]>
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the version 2 of the GNU General Public License
7+
* as published by the Free Software Foundation
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
#include <linux/can/dev.h>
19+
#include <linux/can/rx-offload.h>
20+
21+
static int can_rx_offload_napi_poll(struct napi_struct *napi, int quota)
22+
{
23+
struct can_rx_offload *offload = container_of(napi, struct can_rx_offload, napi);
24+
struct net_device *dev = offload->dev;
25+
struct net_device_stats *stats = &dev->stats;
26+
struct sk_buff *skb;
27+
int work_done = 0;
28+
29+
while ((work_done < quota) &&
30+
(skb = skb_dequeue(&offload->skb_queue))) {
31+
struct can_frame *cf = (struct can_frame *)skb->data;
32+
33+
work_done++;
34+
stats->rx_packets++;
35+
stats->rx_bytes += cf->can_dlc;
36+
netif_receive_skb(skb);
37+
}
38+
39+
if (work_done < quota) {
40+
napi_complete_done(napi, work_done);
41+
42+
/* Check if there was another interrupt */
43+
if (!skb_queue_empty(&offload->skb_queue))
44+
napi_reschedule(&offload->napi);
45+
}
46+
47+
can_led_event(offload->dev, CAN_LED_EVENT_RX);
48+
49+
return work_done;
50+
}
51+
52+
static struct sk_buff *can_rx_offload_offload_one(struct can_rx_offload *offload, unsigned int n)
53+
{
54+
struct sk_buff *skb = NULL;
55+
struct can_frame *cf;
56+
int ret;
57+
58+
/* If queue is full or skb not available, read to discard mailbox */
59+
if (likely(skb_queue_len(&offload->skb_queue) <=
60+
offload->skb_queue_len_max))
61+
skb = alloc_can_skb(offload->dev, &cf);
62+
63+
if (!skb) {
64+
struct can_frame cf_overflow;
65+
66+
ret = offload->mailbox_read(offload, &cf_overflow, n);
67+
if (ret)
68+
offload->dev->stats.rx_dropped++;
69+
70+
return NULL;
71+
}
72+
73+
ret = offload->mailbox_read(offload, cf, n);
74+
if (!ret) {
75+
kfree_skb(skb);
76+
return NULL;
77+
}
78+
79+
return skb;
80+
}
81+
82+
int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload)
83+
{
84+
struct sk_buff *skb;
85+
int received = 0;
86+
87+
while ((skb = can_rx_offload_offload_one(offload, 0))) {
88+
skb_queue_tail(&offload->skb_queue, skb);
89+
received++;
90+
}
91+
92+
if (received)
93+
can_rx_offload_schedule(offload);
94+
95+
return received;
96+
}
97+
EXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_fifo);
98+
99+
int can_rx_offload_irq_queue_err_skb(struct can_rx_offload *offload, struct sk_buff *skb)
100+
{
101+
if (skb_queue_len(&offload->skb_queue) >
102+
offload->skb_queue_len_max)
103+
return -ENOMEM;
104+
105+
skb_queue_tail(&offload->skb_queue, skb);
106+
can_rx_offload_schedule(offload);
107+
108+
return 0;
109+
}
110+
EXPORT_SYMBOL_GPL(can_rx_offload_irq_queue_err_skb);
111+
112+
static int can_rx_offload_init_queue(struct net_device *dev, struct can_rx_offload *offload, unsigned int weight)
113+
{
114+
offload->dev = dev;
115+
116+
/* Limit queue len to 4x the weight (rounted to next power of two) */
117+
offload->skb_queue_len_max = 2 << fls(weight);
118+
offload->skb_queue_len_max *= 4;
119+
skb_queue_head_init(&offload->skb_queue);
120+
121+
can_rx_offload_reset(offload);
122+
netif_napi_add(dev, &offload->napi, can_rx_offload_napi_poll, weight);
123+
124+
dev_dbg(dev->dev.parent, "%s: skb_queue_len_max=%d\n",
125+
__func__, offload->skb_queue_len_max);
126+
127+
return 0;
128+
}
129+
130+
int can_rx_offload_add_fifo(struct net_device *dev, struct can_rx_offload *offload, unsigned int weight)
131+
{
132+
if (!offload->mailbox_read)
133+
return -EINVAL;
134+
135+
return can_rx_offload_init_queue(dev, offload, weight);
136+
}
137+
EXPORT_SYMBOL_GPL(can_rx_offload_add_fifo);
138+
139+
void can_rx_offload_enable(struct can_rx_offload *offload)
140+
{
141+
can_rx_offload_reset(offload);
142+
napi_enable(&offload->napi);
143+
}
144+
EXPORT_SYMBOL_GPL(can_rx_offload_enable);
145+
146+
void can_rx_offload_del(struct can_rx_offload *offload)
147+
{
148+
netif_napi_del(&offload->napi);
149+
skb_queue_purge(&offload->skb_queue);
150+
}
151+
EXPORT_SYMBOL_GPL(can_rx_offload_del);
152+
153+
void can_rx_offload_reset(struct can_rx_offload *offload)
154+
{
155+
}
156+
EXPORT_SYMBOL_GPL(can_rx_offload_reset);

include/linux/can/rx-offload.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* linux/can/rx-offload.h
3+
*
4+
* Copyright (c) 2014 David Jander, Protonic Holland
5+
* Copyright (c) 2014-2017 Pengutronix, Marc Kleine-Budde <[email protected]>
6+
*
7+
* This program is free software; you can redistribute it and/or modify
8+
* it under the terms of the version 2 of the GNU General Public License
9+
* as published by the Free Software Foundation
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*/
16+
17+
#ifndef _CAN_RX_OFFLOAD_H
18+
#define _CAN_RX_OFFLOAD_H
19+
20+
#include <linux/netdevice.h>
21+
#include <linux/can.h>
22+
23+
struct can_rx_offload {
24+
struct net_device *dev;
25+
26+
unsigned int (*mailbox_read)(struct can_rx_offload *offload, struct can_frame *cf, unsigned int mb);
27+
28+
struct sk_buff_head skb_queue;
29+
u32 skb_queue_len_max;
30+
31+
struct napi_struct napi;
32+
};
33+
34+
int can_rx_offload_add_fifo(struct net_device *dev, struct can_rx_offload *offload, unsigned int weight);
35+
int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload);
36+
int can_rx_offload_irq_queue_err_skb(struct can_rx_offload *offload, struct sk_buff *skb);
37+
void can_rx_offload_reset(struct can_rx_offload *offload);
38+
void can_rx_offload_del(struct can_rx_offload *offload);
39+
void can_rx_offload_enable(struct can_rx_offload *offload);
40+
41+
static inline void can_rx_offload_schedule(struct can_rx_offload *offload)
42+
{
43+
napi_schedule(&offload->napi);
44+
}
45+
46+
static inline void can_rx_offload_disable(struct can_rx_offload *offload)
47+
{
48+
napi_disable(&offload->napi);
49+
}
50+
51+
#endif /* !_CAN_RX_OFFLOAD_H */

0 commit comments

Comments
 (0)