linux/net/lapb/lapb_timer.c
Xie He b491e6a739 net: lapb: Add locking to the lapb module
In the lapb module, the timers may run concurrently with other code in
this module, and there is currently no locking to prevent the code from
racing on "struct lapb_cb". This patch adds locking to prevent racing.

1. Add "spinlock_t lock" to "struct lapb_cb"; Add "spin_lock_bh" and
"spin_unlock_bh" to APIs, timer functions and notifier functions.

2. Add "bool t1timer_stop, t2timer_stop" to "struct lapb_cb" to make us
able to ask running timers to abort; Modify "lapb_stop_t1timer" and
"lapb_stop_t2timer" to make them able to abort running timers;
Modify "lapb_t2timer_expiry" and "lapb_t1timer_expiry" to make them
abort after they are stopped by "lapb_stop_t1timer", "lapb_stop_t2timer",
and "lapb_start_t1timer", "lapb_start_t2timer".

3. Let lapb_unregister wait for other API functions and running timers
to stop.

4. The lapb_device_event function calls lapb_disconnect_request. In
order to avoid trying to hold the lock twice, add a new function named
"__lapb_disconnect_request" which assumes the lock is held, and make
it called by lapb_disconnect_request and lapb_device_event.

Fixes: 1da177e4c3 ("Linux-2.6.12-rc2")
Cc: Martin Schiller <ms@dev.tdt.de>
Signed-off-by: Xie He <xie.he.0141@gmail.com>
Link: https://lore.kernel.org/r/20210126040939.69995-1-xie.he.0141@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2021-01-26 17:53:45 -08:00

202 lines
4.7 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* LAPB release 002
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* History
* LAPB 001 Jonathan Naylor Started Coding
* LAPB 002 Jonathan Naylor New timer architecture.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/inet.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <linux/uaccess.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <net/lapb.h>
static void lapb_t1timer_expiry(struct timer_list *);
static void lapb_t2timer_expiry(struct timer_list *);
void lapb_start_t1timer(struct lapb_cb *lapb)
{
del_timer(&lapb->t1timer);
lapb->t1timer.function = lapb_t1timer_expiry;
lapb->t1timer.expires = jiffies + lapb->t1;
lapb->t1timer_stop = false;
add_timer(&lapb->t1timer);
}
void lapb_start_t2timer(struct lapb_cb *lapb)
{
del_timer(&lapb->t2timer);
lapb->t2timer.function = lapb_t2timer_expiry;
lapb->t2timer.expires = jiffies + lapb->t2;
lapb->t2timer_stop = false;
add_timer(&lapb->t2timer);
}
void lapb_stop_t1timer(struct lapb_cb *lapb)
{
lapb->t1timer_stop = true;
del_timer(&lapb->t1timer);
}
void lapb_stop_t2timer(struct lapb_cb *lapb)
{
lapb->t2timer_stop = true;
del_timer(&lapb->t2timer);
}
int lapb_t1timer_running(struct lapb_cb *lapb)
{
return timer_pending(&lapb->t1timer);
}
static void lapb_t2timer_expiry(struct timer_list *t)
{
struct lapb_cb *lapb = from_timer(lapb, t, t2timer);
spin_lock_bh(&lapb->lock);
if (timer_pending(&lapb->t2timer)) /* A new timer has been set up */
goto out;
if (lapb->t2timer_stop) /* The timer has been stopped */
goto out;
if (lapb->condition & LAPB_ACK_PENDING_CONDITION) {
lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
lapb_timeout_response(lapb);
}
out:
spin_unlock_bh(&lapb->lock);
}
static void lapb_t1timer_expiry(struct timer_list *t)
{
struct lapb_cb *lapb = from_timer(lapb, t, t1timer);
spin_lock_bh(&lapb->lock);
if (timer_pending(&lapb->t1timer)) /* A new timer has been set up */
goto out;
if (lapb->t1timer_stop) /* The timer has been stopped */
goto out;
switch (lapb->state) {
/*
* If we are a DCE, send DM up to N2 times, then switch to
* STATE_1 and send SABM(E).
*/
case LAPB_STATE_0:
if (lapb->mode & LAPB_DCE &&
lapb->n2count != lapb->n2) {
lapb->n2count++;
lapb_send_control(lapb, LAPB_DM, LAPB_POLLOFF, LAPB_RESPONSE);
} else {
lapb->state = LAPB_STATE_1;
lapb_establish_data_link(lapb);
}
break;
/*
* Awaiting connection state, send SABM(E), up to N2 times.
*/
case LAPB_STATE_1:
if (lapb->n2count == lapb->n2) {
lapb_clear_queues(lapb);
lapb->state = LAPB_STATE_0;
lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
lapb_dbg(0, "(%p) S1 -> S0\n", lapb->dev);
goto out;
} else {
lapb->n2count++;
if (lapb->mode & LAPB_EXTENDED) {
lapb_dbg(1, "(%p) S1 TX SABME(1)\n",
lapb->dev);
lapb_send_control(lapb, LAPB_SABME, LAPB_POLLON, LAPB_COMMAND);
} else {
lapb_dbg(1, "(%p) S1 TX SABM(1)\n",
lapb->dev);
lapb_send_control(lapb, LAPB_SABM, LAPB_POLLON, LAPB_COMMAND);
}
}
break;
/*
* Awaiting disconnection state, send DISC, up to N2 times.
*/
case LAPB_STATE_2:
if (lapb->n2count == lapb->n2) {
lapb_clear_queues(lapb);
lapb->state = LAPB_STATE_0;
lapb_disconnect_confirmation(lapb, LAPB_TIMEDOUT);
lapb_dbg(0, "(%p) S2 -> S0\n", lapb->dev);
goto out;
} else {
lapb->n2count++;
lapb_dbg(1, "(%p) S2 TX DISC(1)\n", lapb->dev);
lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
}
break;
/*
* Data transfer state, restransmit I frames, up to N2 times.
*/
case LAPB_STATE_3:
if (lapb->n2count == lapb->n2) {
lapb_clear_queues(lapb);
lapb->state = LAPB_STATE_0;
lapb_stop_t2timer(lapb);
lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
lapb_dbg(0, "(%p) S3 -> S0\n", lapb->dev);
goto out;
} else {
lapb->n2count++;
lapb_requeue_frames(lapb);
lapb_kick(lapb);
}
break;
/*
* Frame reject state, restransmit FRMR frames, up to N2 times.
*/
case LAPB_STATE_4:
if (lapb->n2count == lapb->n2) {
lapb_clear_queues(lapb);
lapb->state = LAPB_STATE_0;
lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
lapb_dbg(0, "(%p) S4 -> S0\n", lapb->dev);
goto out;
} else {
lapb->n2count++;
lapb_transmit_frmr(lapb);
}
break;
}
lapb_start_t1timer(lapb);
out:
spin_unlock_bh(&lapb->lock);
}