diff options
author | Oliver Hartkopp <socketcan@hartkopp.net> | 2015-04-01 07:50:28 +0200 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2015-04-01 11:27:41 +0200 |
commit | 514ac99c64b22d83b52dfee3b8becaa69a92bc4a (patch) | |
tree | dc44e61b13c9c7279ad7b5be1195f5cebeb422c2 /net/can | |
parent | can: ems_usb: mark timestamp as little endian (diff) | |
download | linux-514ac99c64b22d83b52dfee3b8becaa69a92bc4a.tar.xz linux-514ac99c64b22d83b52dfee3b8becaa69a92bc4a.zip |
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"); * storing the single filter in dfilter, to avoid using dynamic memory. */ +struct uniqframe { + ktime_t tstamp; + const struct sk_buff *skb; +}; + struct raw_sock { struct sock sk; int bound; @@ -86,6 +91,7 @@ struct raw_sock { struct can_filter dfilter; /* default/single filter */ struct can_filter *filter; /* pointer to filter(s) */ can_err_mask_t err_mask; + struct uniqframe __percpu *uniq; }; /* @@ -123,6 +129,15 @@ static void raw_rcv(struct sk_buff *oskb, void *data) if (!ro->fd_frames && oskb->len != CAN_MTU) return; + /* eliminate multiple filter matches for the same skb */ + if (this_cpu_ptr(ro->uniq)->skb == oskb && + ktime_equal(this_cpu_ptr(ro->uniq)->tstamp, oskb->tstamp)) { + return; + } else { + this_cpu_ptr(ro->uniq)->skb = oskb; + this_cpu_ptr(ro->uniq)->tstamp = oskb->tstamp; + } + /* clone the given skb to be able to enqueue it into the rcv queue */ skb = skb_clone(oskb, GFP_ATOMIC); if (!skb) @@ -297,6 +312,11 @@ static int raw_init(struct sock *sk) ro->recv_own_msgs = 0; ro->fd_frames = 0; + /* alloc_percpu provides zero'ed memory */ + ro->uniq = alloc_percpu(struct uniqframe); + if (unlikely(!ro->uniq)) + return -ENOMEM; + /* set notifier */ ro->notifier.notifier_call = raw_notifier; @@ -339,6 +359,7 @@ static int raw_release(struct socket *sock) ro->ifindex = 0; ro->bound = 0; ro->count = 0; + free_percpu(ro->uniq); sock_orphan(sk); sock->sk = NULL; |