mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
Merge patch series "net: can: isotp-fixes"
Oliver Hartkopp <socketcan@hartkopp.net> says: As sashiko-bot was not able to check the second patch this bundle is re-posted with b4 preparation. Link: https://patch.msgid.link/20260712-isotp-fixes-v10-0-793a1b1ce17f@hartkopp.net [mkl: added stable@k.o on Cc, converted Link: -> Closes:] Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
298
net/can/isotp.c
298
net/can/isotp.c
@@ -152,11 +152,13 @@ struct isotp_sock {
|
||||
struct sock sk;
|
||||
int bound;
|
||||
int ifindex;
|
||||
struct net_device *dev;
|
||||
netdevice_tracker dev_tracker;
|
||||
canid_t txid;
|
||||
canid_t rxid;
|
||||
ktime_t tx_gap;
|
||||
ktime_t lastrxcf_tstamp;
|
||||
struct hrtimer rxtimer, txtimer, txfrtimer;
|
||||
struct hrtimer rxtimer, txtimer, txfrtimer, echotimer;
|
||||
struct can_isotp_options opt;
|
||||
struct can_isotp_fc_options rxfc, txfc;
|
||||
struct can_isotp_ll_options ll;
|
||||
@@ -164,6 +166,7 @@ struct isotp_sock {
|
||||
u32 force_tx_stmin;
|
||||
u32 force_rx_stmin;
|
||||
u32 cfecho; /* consecutive frame echo tag */
|
||||
u32 tx_gen; /* generation, bumped per new tx transfer */
|
||||
struct tpcon rx, tx;
|
||||
struct list_head notifier;
|
||||
wait_queue_head_t wait;
|
||||
@@ -376,6 +379,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
|
||||
|
||||
hrtimer_cancel(&so->txtimer);
|
||||
|
||||
/* isotp_tx_timeout() may have given up on this job while
|
||||
* hrtimer_cancel() above waited for it to finish; so->rx_lock
|
||||
* (held by our caller isotp_rcv()) rules out a concurrent claim,
|
||||
* so a plain recheck is enough here.
|
||||
*/
|
||||
if (so->tx.state != ISOTP_WAIT_FC &&
|
||||
so->tx.state != ISOTP_WAIT_FIRST_FC)
|
||||
return 1;
|
||||
|
||||
if ((cf->len < ae + FC_CONTENT_SZ) ||
|
||||
((so->opt.flags & ISOTP_CHECK_PADDING) &&
|
||||
check_pad(so, cf, ae + FC_CONTENT_SZ, so->opt.rxpad_content))) {
|
||||
@@ -422,7 +434,7 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
|
||||
so->tx.bs = 0;
|
||||
so->tx.state = ISOTP_SENDING;
|
||||
/* send CF frame and enable echo timeout handling */
|
||||
hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
|
||||
hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
|
||||
HRTIMER_MODE_REL_SOFT);
|
||||
isotp_send_cframe(so);
|
||||
break;
|
||||
@@ -575,6 +587,14 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae,
|
||||
|
||||
hrtimer_cancel(&so->rxtimer);
|
||||
|
||||
/* isotp_rx_timer_handler() may have raced us for so->rx.state
|
||||
* while hrtimer_cancel() above waited for it to finish, already
|
||||
* reporting ETIMEDOUT and resetting the reception; don't process
|
||||
* this CF into a reassembly that has already been given up on.
|
||||
*/
|
||||
if (so->rx.state != ISOTP_WAIT_DATA)
|
||||
return 1;
|
||||
|
||||
/* CFs are never longer than the FF */
|
||||
if (cf->len > so->rx.ll_dl)
|
||||
return 1;
|
||||
@@ -870,20 +890,36 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
|
||||
struct canfd_frame *cf = (struct canfd_frame *)skb->data;
|
||||
|
||||
/* only handle my own local echo CF/SF skb's (no FF!) */
|
||||
if (skb->sk != sk || so->cfecho != *(u32 *)cf->data)
|
||||
if (skb->sk != sk)
|
||||
return;
|
||||
|
||||
/* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so->rx_lock
|
||||
* (no isotp_rcv() caller here), so take it ourselves
|
||||
*/
|
||||
spin_lock(&so->rx_lock);
|
||||
|
||||
/* so->cfecho may since belong to a new transfer; recheck under lock */
|
||||
if (so->cfecho != *(u32 *)cf->data)
|
||||
goto out_unlock;
|
||||
|
||||
/* cancel local echo timeout */
|
||||
hrtimer_cancel(&so->txtimer);
|
||||
hrtimer_cancel(&so->echotimer);
|
||||
|
||||
/* local echo skb with consecutive frame has been consumed */
|
||||
so->cfecho = 0;
|
||||
|
||||
/* claiming a transfer also takes so->rx_lock, so a plain recheck
|
||||
* is enough: so->tx.state can't have flipped to ISOTP_SENDING for
|
||||
* a new claim while we're still in here
|
||||
*/
|
||||
if (so->tx.state != ISOTP_SENDING)
|
||||
goto out_unlock;
|
||||
|
||||
if (so->tx.idx >= so->tx.len) {
|
||||
/* we are done */
|
||||
so->tx.state = ISOTP_IDLE;
|
||||
wake_up_interruptible(&so->wait);
|
||||
return;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {
|
||||
@@ -891,53 +927,83 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
|
||||
so->tx.state = ISOTP_WAIT_FC;
|
||||
hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
|
||||
HRTIMER_MODE_REL_SOFT);
|
||||
return;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* no gap between data frames needed => use burst mode */
|
||||
if (!so->tx_gap) {
|
||||
/* enable echo timeout handling */
|
||||
hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
|
||||
hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
|
||||
HRTIMER_MODE_REL_SOFT);
|
||||
isotp_send_cframe(so);
|
||||
return;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* start timer to send next consecutive frame with correct delay */
|
||||
hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT);
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&so->rx_lock);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
|
||||
/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get
|
||||
* cancelled under so->rx_lock elsewhere, so this must stay lock-free to
|
||||
* avoid deadlocking with that; uses so->tx_gen instead to avoid tainting
|
||||
* a new transfer with an error from the one that just timed out.
|
||||
*/
|
||||
static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so)
|
||||
{
|
||||
struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
|
||||
txtimer);
|
||||
struct sock *sk = &so->sk;
|
||||
u32 gen = READ_ONCE(so->tx_gen);
|
||||
u32 old_state = READ_ONCE(so->tx.state);
|
||||
|
||||
/* don't handle timeouts in IDLE or SHUTDOWN state */
|
||||
if (so->tx.state == ISOTP_IDLE || so->tx.state == ISOTP_SHUTDOWN)
|
||||
if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN)
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
/* only claim the timeout if the state is still unchanged */
|
||||
if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state)
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
/* we did not get any flow control or echo frame in time */
|
||||
|
||||
/* report 'communication error on send' */
|
||||
sk->sk_err = ECOMM;
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk_error_report(sk);
|
||||
if (READ_ONCE(so->tx_gen) == gen) {
|
||||
/* report 'communication error on send' */
|
||||
sk->sk_err = ECOMM;
|
||||
if (!sock_flag(sk, SOCK_DEAD))
|
||||
sk_error_report(sk);
|
||||
}
|
||||
|
||||
/* reset tx state */
|
||||
so->tx.state = ISOTP_IDLE;
|
||||
wake_up_interruptible(&so->wait);
|
||||
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/* so->txtimer: fires when a Flow Control frame does not arrive in time */
|
||||
static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
|
||||
{
|
||||
struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
|
||||
txtimer);
|
||||
|
||||
return isotp_tx_timeout(so);
|
||||
}
|
||||
|
||||
/* so->echotimer: fires when a sent CF/SF's local echo does not arrive */
|
||||
static enum hrtimer_restart isotp_echo_timer_handler(struct hrtimer *hrtimer)
|
||||
{
|
||||
struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
|
||||
echotimer);
|
||||
|
||||
return isotp_tx_timeout(so);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer)
|
||||
{
|
||||
struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
|
||||
txfrtimer);
|
||||
|
||||
/* start echo timeout handling and cover below protocol error */
|
||||
hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
|
||||
hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
|
||||
HRTIMER_MODE_REL_SOFT);
|
||||
|
||||
/* cfecho should be consumed by isotp_rcv_echo() here */
|
||||
@@ -958,13 +1024,24 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
|
||||
int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
|
||||
int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0;
|
||||
s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
|
||||
struct hrtimer *tx_hrt = &so->echotimer;
|
||||
u32 new_state = ISOTP_SENDING;
|
||||
int off;
|
||||
int err;
|
||||
|
||||
if (!so->bound || so->tx.state == ISOTP_SHUTDOWN)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
while (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) {
|
||||
/* claim the socket under so->rx_lock: this serializes the claim
|
||||
* with the RX path and with sendmsg()'s own error paths below, so
|
||||
* none of them can ever see a transfer mid-claim
|
||||
*/
|
||||
for (;;) {
|
||||
spin_lock_bh(&so->rx_lock);
|
||||
if (READ_ONCE(so->tx.state) == ISOTP_IDLE)
|
||||
break;
|
||||
spin_unlock_bh(&so->rx_lock);
|
||||
|
||||
/* we do not support multiple buffers - for now */
|
||||
if (msg->msg_flags & MSG_DONTWAIT)
|
||||
return -EAGAIN;
|
||||
@@ -973,9 +1050,29 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
/* wait for complete transmission of current pdu */
|
||||
err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
|
||||
err = wait_event_interruptible(so->wait,
|
||||
so->tx.state == ISOTP_IDLE);
|
||||
if (err)
|
||||
goto err_event_drop;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* new transfer: bump so->tx_gen and drain the old one's timers,
|
||||
* still under the so->rx_lock we just claimed the socket with
|
||||
*/
|
||||
WRITE_ONCE(so->tx.state, ISOTP_SENDING);
|
||||
WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1);
|
||||
hrtimer_cancel(&so->txtimer);
|
||||
hrtimer_cancel(&so->echotimer);
|
||||
hrtimer_cancel(&so->txfrtimer);
|
||||
so->cfecho = 0;
|
||||
spin_unlock_bh(&so->rx_lock);
|
||||
|
||||
/* so->bound is only checked once above - a wakeup may have
|
||||
* unbound/rebound the socket meanwhile, so re-validate it
|
||||
*/
|
||||
if (!so->bound) {
|
||||
err = -EADDRNOTAVAIL;
|
||||
goto err_out_drop;
|
||||
}
|
||||
|
||||
/* PDU size > default => try max_pdu_size */
|
||||
@@ -1086,18 +1183,33 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
|
||||
so->cfecho = *(u32 *)cf->data;
|
||||
} else {
|
||||
/* standard flow control check */
|
||||
so->tx.state = ISOTP_WAIT_FIRST_FC;
|
||||
new_state = ISOTP_WAIT_FIRST_FC;
|
||||
|
||||
/* start timeout for FC */
|
||||
hrtimer_sec = ISOTP_FC_TIMEOUT;
|
||||
tx_hrt = &so->txtimer;
|
||||
|
||||
/* no CF echo tag for isotp_rcv_echo() (FF-mode) */
|
||||
so->cfecho = 0;
|
||||
}
|
||||
}
|
||||
|
||||
hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
|
||||
spin_lock_bh(&so->rx_lock);
|
||||
if (so->tx.state == ISOTP_SHUTDOWN) {
|
||||
/* isotp_release() has since taken over and already drained
|
||||
* our timers - don't send into a socket that's going away
|
||||
*/
|
||||
spin_unlock_bh(&so->rx_lock);
|
||||
kfree_skb(skb);
|
||||
dev_put(dev);
|
||||
wake_up_interruptible(&so->wait);
|
||||
return -EADDRNOTAVAIL;
|
||||
}
|
||||
/* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */
|
||||
so->tx.state = new_state;
|
||||
hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0),
|
||||
HRTIMER_MODE_REL_SOFT);
|
||||
spin_unlock_bh(&so->rx_lock);
|
||||
|
||||
/* send the first or only CAN frame */
|
||||
cf->flags = so->ll.tx_flags;
|
||||
@@ -1110,13 +1222,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
|
||||
pr_notice_once("can-isotp: %s: can_send_ret %pe\n",
|
||||
__func__, ERR_PTR(err));
|
||||
|
||||
spin_lock_bh(&so->rx_lock);
|
||||
/* no transmission -> no timeout monitoring */
|
||||
hrtimer_cancel(&so->txtimer);
|
||||
|
||||
/* reset consecutive frame echo tag */
|
||||
so->cfecho = 0;
|
||||
|
||||
goto err_out_drop;
|
||||
hrtimer_cancel(tx_hrt);
|
||||
goto err_out_drop_locked;
|
||||
}
|
||||
|
||||
if (wait_tx_done) {
|
||||
@@ -1132,14 +1241,21 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
|
||||
|
||||
return size;
|
||||
|
||||
err_out_drop:
|
||||
/* claimed but nothing sent yet - no timer to cancel */
|
||||
spin_lock_bh(&so->rx_lock);
|
||||
goto err_out_drop_locked;
|
||||
err_event_drop:
|
||||
/* got signal: force tx state machine to be idle */
|
||||
so->tx.state = ISOTP_IDLE;
|
||||
/* interrupted waiting on our own transfer - drain its timers */
|
||||
spin_lock_bh(&so->rx_lock);
|
||||
hrtimer_cancel(&so->txfrtimer);
|
||||
hrtimer_cancel(&so->txtimer);
|
||||
err_out_drop:
|
||||
/* drop this PDU and unlock a potential wait queue */
|
||||
hrtimer_cancel(&so->echotimer);
|
||||
err_out_drop_locked:
|
||||
/* release the claim; so->rx_lock still held from above */
|
||||
so->cfecho = 0;
|
||||
so->tx.state = ISOTP_IDLE;
|
||||
spin_unlock_bh(&so->rx_lock);
|
||||
wake_up_interruptible(&so->wait);
|
||||
|
||||
return err;
|
||||
@@ -1201,13 +1317,20 @@ static int isotp_release(struct socket *sock)
|
||||
so = isotp_sk(sk);
|
||||
net = sock_net(sk);
|
||||
|
||||
/* wait for complete transmission of current pdu */
|
||||
while (wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0 &&
|
||||
cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SHUTDOWN) != ISOTP_IDLE)
|
||||
/* best-effort: wait for a running pdu to finish, but don't block on
|
||||
* it forever - give up after the first signal
|
||||
*/
|
||||
while (so->tx.state != ISOTP_IDLE &&
|
||||
wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0)
|
||||
;
|
||||
|
||||
/* force state machines to be idle also when a signal occurred */
|
||||
/* claim the socket under so->rx_lock like sendmsg() does, so its
|
||||
* claim can't race the forced ISOTP_SHUTDOWN below; force it
|
||||
* unconditionally, even when a signal cut the wait above short
|
||||
*/
|
||||
spin_lock_bh(&so->rx_lock);
|
||||
so->tx.state = ISOTP_SHUTDOWN;
|
||||
spin_unlock_bh(&so->rx_lock);
|
||||
so->rx.state = ISOTP_IDLE;
|
||||
|
||||
spin_lock(&isotp_notifier_lock);
|
||||
@@ -1219,35 +1342,42 @@ static int isotp_release(struct socket *sock)
|
||||
list_del(&so->notifier);
|
||||
spin_unlock(&isotp_notifier_lock);
|
||||
|
||||
rtnl_lock();
|
||||
lock_sock(sk);
|
||||
|
||||
/* remove current filters & unregister */
|
||||
if (so->bound) {
|
||||
if (so->ifindex) {
|
||||
struct net_device *dev;
|
||||
/* remove current filters & unregister
|
||||
* tracked reference so->dev is taken at bind() time with rtnl_lock
|
||||
*/
|
||||
if (so->bound && so->dev) {
|
||||
if (isotp_register_rxid(so))
|
||||
can_rx_unregister(net, so->dev, so->rxid,
|
||||
SINGLE_MASK(so->rxid),
|
||||
isotp_rcv, sk);
|
||||
|
||||
dev = dev_get_by_index(net, so->ifindex);
|
||||
if (dev) {
|
||||
if (isotp_register_rxid(so))
|
||||
can_rx_unregister(net, dev, so->rxid,
|
||||
SINGLE_MASK(so->rxid),
|
||||
isotp_rcv, sk);
|
||||
|
||||
can_rx_unregister(net, dev, so->txid,
|
||||
SINGLE_MASK(so->txid),
|
||||
isotp_rcv_echo, sk);
|
||||
dev_put(dev);
|
||||
synchronize_rcu();
|
||||
}
|
||||
}
|
||||
can_rx_unregister(net, so->dev, so->txid,
|
||||
SINGLE_MASK(so->txid),
|
||||
isotp_rcv_echo, sk);
|
||||
netdev_put(so->dev, &so->dev_tracker);
|
||||
}
|
||||
|
||||
hrtimer_cancel(&so->txfrtimer);
|
||||
hrtimer_cancel(&so->txtimer);
|
||||
hrtimer_cancel(&so->rxtimer);
|
||||
|
||||
so->ifindex = 0;
|
||||
so->bound = 0;
|
||||
so->dev = NULL;
|
||||
|
||||
rtnl_unlock();
|
||||
|
||||
/* Always wait for a grace period before touching the timers below.
|
||||
* A concurrent NETDEV_UNREGISTER may have already unregistered our
|
||||
* filters and cleared so->bound in isotp_notify() without waiting
|
||||
* for in-flight isotp_rcv() callers to finish, so this call must not
|
||||
* be skipped just because so->bound is already 0 here.
|
||||
*/
|
||||
synchronize_rcu();
|
||||
|
||||
hrtimer_cancel(&so->txfrtimer);
|
||||
hrtimer_cancel(&so->txtimer);
|
||||
hrtimer_cancel(&so->echotimer);
|
||||
hrtimer_cancel(&so->rxtimer);
|
||||
|
||||
sock_orphan(sk);
|
||||
sock->sk = NULL;
|
||||
@@ -1303,6 +1433,7 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
|
||||
if (!addr->can_ifindex)
|
||||
return -ENODEV;
|
||||
|
||||
rtnl_lock();
|
||||
lock_sock(sk);
|
||||
|
||||
if (so->bound) {
|
||||
@@ -1310,6 +1441,17 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* A transmission or reception that outlived a previous binding
|
||||
* (unbound by NETDEV_UNREGISTER) may still be draining; the FC/echo
|
||||
* and RX watchdog timers bound how long this takes. Checked together
|
||||
* with so->bound in the same lock_sock() section above, so there is
|
||||
* no window in which a concurrent isotp_notify() could be missed.
|
||||
*/
|
||||
if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) {
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* ensure different CAN IDs when the rx_id is to be registered */
|
||||
if (isotp_register_rxid(so) && rx_id == tx_id) {
|
||||
err = -EADDRNOTAVAIL;
|
||||
@@ -1322,14 +1464,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
|
||||
goto out;
|
||||
}
|
||||
if (dev->type != ARPHRD_CAN) {
|
||||
dev_put(dev);
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
goto out_put_dev;
|
||||
}
|
||||
if (READ_ONCE(dev->mtu) < so->ll.mtu) {
|
||||
dev_put(dev);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
goto out_put_dev;
|
||||
}
|
||||
if (!(dev->flags & IFF_UP))
|
||||
notify_enetdown = 1;
|
||||
@@ -1347,16 +1487,25 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
|
||||
can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
|
||||
isotp_rcv_echo, sk, "isotpe", sk);
|
||||
|
||||
dev_put(dev);
|
||||
|
||||
/* switch to new settings */
|
||||
so->ifindex = ifindex;
|
||||
so->rxid = rx_id;
|
||||
so->txid = tx_id;
|
||||
so->bound = 1;
|
||||
|
||||
/* bind() ok -> hold a reference for so->dev so that isotp_release()
|
||||
* can safely reach the device later, even if a concurrent
|
||||
* NETDEV_UNREGISTER has already unlisted it by ifindex.
|
||||
*/
|
||||
so->dev = dev;
|
||||
netdev_hold(so->dev, &so->dev_tracker, GFP_KERNEL);
|
||||
|
||||
out_put_dev:
|
||||
/* remove potential reference from dev_get_by_index() */
|
||||
dev_put(dev);
|
||||
out:
|
||||
release_sock(sk);
|
||||
rtnl_unlock();
|
||||
|
||||
if (notify_enetdown) {
|
||||
sk->sk_err = ENETDOWN;
|
||||
@@ -1559,7 +1708,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
|
||||
if (!net_eq(dev_net(dev), sock_net(sk)))
|
||||
return;
|
||||
|
||||
if (so->ifindex != dev->ifindex)
|
||||
if (so->dev != dev)
|
||||
return;
|
||||
|
||||
switch (msg) {
|
||||
@@ -1575,10 +1724,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
|
||||
can_rx_unregister(dev_net(dev), dev, so->txid,
|
||||
SINGLE_MASK(so->txid),
|
||||
isotp_rcv_echo, sk);
|
||||
netdev_put(so->dev, &so->dev_tracker);
|
||||
}
|
||||
|
||||
so->ifindex = 0;
|
||||
so->bound = 0;
|
||||
so->dev = NULL;
|
||||
release_sock(sk);
|
||||
|
||||
sk->sk_err = ENODEV;
|
||||
@@ -1638,6 +1789,7 @@ static int isotp_init(struct sock *sk)
|
||||
|
||||
so->ifindex = 0;
|
||||
so->bound = 0;
|
||||
so->dev = NULL;
|
||||
|
||||
so->opt.flags = CAN_ISOTP_DEFAULT_FLAGS;
|
||||
so->opt.ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS;
|
||||
@@ -1664,10 +1816,14 @@ static int isotp_init(struct sock *sk)
|
||||
so->rx.buflen = ARRAY_SIZE(so->rx.sbuf);
|
||||
so->tx.buflen = ARRAY_SIZE(so->tx.sbuf);
|
||||
|
||||
hrtimer_setup(&so->rxtimer, isotp_rx_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
|
||||
hrtimer_setup(&so->txtimer, isotp_tx_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
|
||||
hrtimer_setup(&so->txfrtimer, isotp_txfr_timer_handler, CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_REL_SOFT);
|
||||
hrtimer_setup(&so->rxtimer, isotp_rx_timer_handler,
|
||||
CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
|
||||
hrtimer_setup(&so->txtimer, isotp_tx_timer_handler,
|
||||
CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
|
||||
hrtimer_setup(&so->echotimer, isotp_echo_timer_handler,
|
||||
CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
|
||||
hrtimer_setup(&so->txfrtimer, isotp_txfr_timer_handler,
|
||||
CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
|
||||
|
||||
init_waitqueue_head(&so->wait);
|
||||
spin_lock_init(&so->rx_lock);
|
||||
|
||||
Reference in New Issue
Block a user