diff options
author | Oliver Hartkopp <socketcan@hartkopp.net> | 2015-04-01 01:50:28 -0400 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2015-04-01 05:27:41 -0400 |
commit | 514ac99c64b22d83b52dfee3b8becaa69a92bc4a (patch) | |
tree | dc44e61b13c9c7279ad7b5be1195f5cebeb422c2 /net/can | |
parent | a0bc163a7cf2ceb413a76cfd7149c6591db829ea (diff) |
can: fix multiple delivery of a single CAN frame for overlapping CAN filters
The CAN_RAW socket can set multiple CAN identifier specific filters that lead
to multiple filters in the af_can.c filter processing. These filters are
indenpendent from each other which leads to logical OR'ed filters when applied.
This patch makes sure that every CAN frame which is filtered for a specific
socket is only delivered once to the user space. This is independent from the
number of matching CAN filters of this socket.
As the raw_rcv() function is executed from NET_RX softirq the introduced
variables are implemented as per-CPU variables to avoid extensive locking at
CAN frame reception time.
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Diffstat (limited to 'net/can')
-rw-r--r-- | net/can/raw.c | 21 |
1 files changed, 21 insertions, 0 deletions
diff --git a/net/can/raw.c b/net/can/raw.c index 63ffdb0f3a23..0c8d537b59b8 100644 --- a/net/can/raw.c +++ b/net/can/raw.c | |||
@@ -74,6 +74,11 @@ MODULE_ALIAS("can-proto-1"); | |||
74 | * storing the single filter in dfilter, to avoid using dynamic memory. | 74 | * storing the single filter in dfilter, to avoid using dynamic memory. |
75 | */ | 75 | */ |
76 | 76 | ||
77 | struct uniqframe { | ||
78 | ktime_t tstamp; | ||
79 | const struct sk_buff *skb; | ||
80 | }; | ||
81 | |||
77 | struct raw_sock { | 82 | struct raw_sock { |
78 | struct sock sk; | 83 | struct sock sk; |
79 | int bound; | 84 | int bound; |
@@ -86,6 +91,7 @@ struct raw_sock { | |||
86 | struct can_filter dfilter; /* default/single filter */ | 91 | struct can_filter dfilter; /* default/single filter */ |
87 | struct can_filter *filter; /* pointer to filter(s) */ | 92 | struct can_filter *filter; /* pointer to filter(s) */ |
88 | can_err_mask_t err_mask; | 93 | can_err_mask_t err_mask; |
94 | struct uniqframe __percpu *uniq; | ||
89 | }; | 95 | }; |
90 | 96 | ||
91 | /* | 97 | /* |
@@ -123,6 +129,15 @@ static void raw_rcv(struct sk_buff *oskb, void *data) | |||
123 | if (!ro->fd_frames && oskb->len != CAN_MTU) | 129 | if (!ro->fd_frames && oskb->len != CAN_MTU) |
124 | return; | 130 | return; |
125 | 131 | ||
132 | /* eliminate multiple filter matches for the same skb */ | ||
133 | if (this_cpu_ptr(ro->uniq)->skb == oskb && | ||
134 | ktime_equal(this_cpu_ptr(ro->uniq)->tstamp, oskb->tstamp)) { | ||
135 | return; | ||
136 | } else { | ||
137 | this_cpu_ptr(ro->uniq)->skb = oskb; | ||
138 | this_cpu_ptr(ro->uniq)->tstamp = oskb->tstamp; | ||
139 | } | ||
140 | |||
126 | /* clone the given skb to be able to enqueue it into the rcv queue */ | 141 | /* clone the given skb to be able to enqueue it into the rcv queue */ |
127 | skb = skb_clone(oskb, GFP_ATOMIC); | 142 | skb = skb_clone(oskb, GFP_ATOMIC); |
128 | if (!skb) | 143 | if (!skb) |
@@ -297,6 +312,11 @@ static int raw_init(struct sock *sk) | |||
297 | ro->recv_own_msgs = 0; | 312 | ro->recv_own_msgs = 0; |
298 | ro->fd_frames = 0; | 313 | ro->fd_frames = 0; |
299 | 314 | ||
315 | /* alloc_percpu provides zero'ed memory */ | ||
316 | ro->uniq = alloc_percpu(struct uniqframe); | ||
317 | if (unlikely(!ro->uniq)) | ||
318 | return -ENOMEM; | ||
319 | |||
300 | /* set notifier */ | 320 | /* set notifier */ |
301 | ro->notifier.notifier_call = raw_notifier; | 321 | ro->notifier.notifier_call = raw_notifier; |
302 | 322 | ||
@@ -339,6 +359,7 @@ static int raw_release(struct socket *sock) | |||
339 | ro->ifindex = 0; | 359 | ro->ifindex = 0; |
340 | ro->bound = 0; | 360 | ro->bound = 0; |
341 | ro->count = 0; | 361 | ro->count = 0; |
362 | free_percpu(ro->uniq); | ||
342 | 363 | ||
343 | sock_orphan(sk); | 364 | sock_orphan(sk); |
344 | sock->sk = NULL; | 365 | sock->sk = NULL; |