6.12-stable review patch. If anyone has any objections, please let me know. ------------------ From: Bernard Pidoux For a connect request rose_get_neigh() walks the routes that match the address, from the most specific to the least specific, and takes the first neighbour whose link is restarted. A neighbour whose link is down is only used when no matching route at all has a restarted neighbour. A node that has both an exact route to a neighbouring node and a wide route through a relay therefore never uses the exact route once its link is down: the relay is restarted, it is always found first, and since no frame is ever sent over the direct link nothing brings it up again. The calls keep going through the relay, which may have no route other than back to the calling node and then clears them. Seen on a node whose White Pages daemon calls an adjacent node: every call goes to the relay and comes back as CLEAR REQUEST "not obtainable", for as long as the node runs. Keep sending the call to the restarted neighbour, so that a call never waits for a link that may not answer, but when a neighbour of a more specific route, or an earlier neighbour of the same route, was passed over because its link is down, start the restart procedure on that link. If the other end answers, the next call finds the link restarted and uses it. A link that has just failed is left alone for link_fail_timeout, and nothing is done while a restart is under way. Frames in transit are handled as before. Tested in QEMU with a lockdep and KASAN kernel, three nodes A - B - C where B has an exact route to C over their direct link and a wide route through A, and the link B - A is restarted while B - C is not. Before, every call from B to C goes to A and is cleared there, and the direct link stays down. After, the first call still goes to A, the direct link is restarted within 20 ms and the following calls go through it. With C switched off no call waits: they all go to A at once, the restart is attempted once, and after the AX.25 connection gives up the link is left alone for link_fail_timeout. A call from A to C in transit through B gets the same answer before and after. No lockdep, KASAN or refcount report. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Bernard Pidoux (cherry picked from commit cdf5a2eba2bc4f79dabb013a3a935f54aedb85e9) Signed-off-by: Sasha Levin --- include/net/rose.h | 1 + net/rose/rose_link.c | 14 ++++++++++++++ net/rose/rose_route.c | 14 ++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/include/net/rose.h b/include/net/rose.h index 2820b7e4697e6..2fa0331e36f09 100644 --- a/include/net/rose.h +++ b/include/net/rose.h @@ -240,6 +240,7 @@ void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *, void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char, unsigned char); void rose_link_restart(struct rose_neigh *neigh); +void rose_link_bring_up(struct rose_neigh *neigh); void rose_transmit_link(struct sk_buff *, struct rose_neigh *); /* rose_loopback.c */ diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c index 76032148a95eb..9cfd4aa174f81 100644 --- a/net/rose/rose_link.c +++ b/net/rose/rose_link.c @@ -287,6 +287,20 @@ void rose_link_restart(struct rose_neigh *neigh) rose_start_t0timer(neigh); } +/* + * Start the restart procedure on a link that is down without waiting for + * a frame to send over it. Nothing is done while a restart is under way. + */ +void rose_link_bring_up(struct rose_neigh *neigh) +{ + if (neigh->loopback || neigh->restarted || rose_t0timer_running(neigh)) + return; + + rose_transmit_restart_request(neigh); + neigh->dce_mode = 0; + rose_start_t0timer(neigh); +} + void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh) { unsigned char *dptr; diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c index 68a4f2385277b..6a1de0055e28b 100644 --- a/net/rose/rose_route.c +++ b/net/rose/rose_route.c @@ -741,6 +741,7 @@ struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, unsigned char *diagnostic, int route_frame) { struct rose_neigh *res = NULL; + struct rose_neigh *down = NULL; struct rose_node *node; int failed = 0; int i; @@ -752,8 +753,21 @@ struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, if (node->neighbour[i]->restarted) { res = node->neighbour[i]; rose_neigh_hold(node->neighbour[i]); + /* + * A neighbour of a more specific route + * was passed over because its link is + * down: this call does not wait for it, + * but have the link brought up for the + * next ones. + */ + if (down) + rose_link_bring_up(down); goto out; } + /* The first one is the preferred one. */ + if (!route_frame && !down && + !rose_ftimer_running(node->neighbour[i])) + down = node->neighbour[i]; } } } -- 2.53.0