From: Daniel Starke <[email protected]>
After setting up the control channel on both sides the responder side may
want to open a virtual tty to listen on until the initiator starts an
application on a user channels. The current implementation allows the
open() but no other operation, like termios. These fail with EINVAL.
The responder sided application has no means to detect an open by the
initiator sided application this way. And the initiator sided applications
usually expect the responder sided application to listen on the user
channel upon open.
Set the user channel into half-open state on responder side once a user
application opens the virtual tty to allow IO operations on it.
Furthermore, keep the user channel constipated until the initiator side
opens it to give the responder sided application the chance to detect the
new connection and to avoid data loss if the responder sided application
starts sending before the user channel is open.
Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
Cc: [email protected]
Signed-off-by: Daniel Starke <[email protected]>
---
drivers/tty/n_gsm.c | 31 +++++++++++++++++++++++++++++--
1 file changed, 29 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index fd8b86dde525..08fea3e7674d 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -1493,6 +1493,8 @@ static void gsm_dlci_close(struct gsm_dlci *dlci)
if (debug & 8)
pr_debug("DLCI %d goes closed.\n", dlci->addr);
dlci->state = DLCI_CLOSED;
+ /* Prevent us from sending data before the link is up again */
+ dlci->constipated = true;
if (dlci->addr != 0) {
tty_port_tty_hangup(&dlci->port, false);
spin_lock_irqsave(&dlci->lock, flags);
@@ -1522,6 +1524,7 @@ static void gsm_dlci_open(struct gsm_dlci *dlci)
del_timer(&dlci->t1);
/* This will let a tty open continue */
dlci->state = DLCI_OPEN;
+ dlci->constipated = false;
if (debug & 8)
pr_debug("DLCI %d goes open.\n", dlci->addr);
/* Send current modem state */
@@ -1602,6 +1605,25 @@ static void gsm_dlci_begin_open(struct gsm_dlci *dlci)
mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
}
+/**
+ * gsm_dlci_wait_open - wait for channel open procedure
+ * @dlci: DLCI to open
+ *
+ * Wait for a DLCI opening from the other side. Asynchronously wait until
+ * we get a SABM and set off timers and the responses.
+ */
+static void gsm_dlci_wait_open(struct gsm_dlci *dlci)
+{
+ switch (dlci->state) {
+ case DLCI_CLOSED:
+ case DLCI_CLOSING:
+ dlci->state = DLCI_OPENING;
+ break;
+ default:
+ break;
+ }
+}
+
/**
* gsm_dlci_begin_close - start channel open procedure
* @dlci: DLCI to open
@@ -1745,10 +1767,13 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr)
dlci->addr = addr;
dlci->adaption = gsm->adaption;
dlci->state = DLCI_CLOSED;
- if (addr)
+ if (addr) {
dlci->data = gsm_dlci_data;
- else
+ /* Prevent us from sending data before the link is up */
+ dlci->constipated = true;
+ } else {
dlci->data = gsm_dlci_command;
+ }
gsm->dlci[addr] = dlci;
return dlci;
}
@@ -3163,6 +3188,8 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp)
/* Start sending off SABM messages */
if (gsm->initiator)
gsm_dlci_begin_open(dlci);
+ else
+ gsm_dlci_wait_open(dlci);
/* And wait for virtual carrier */
return tty_port_block_til_ready(port, tty, filp);
}
--
2.34.1
From: Daniel Starke <[email protected]>
The current implementation queues up new control and user packets as needed
and processes this queue down to the ldisc in the same code path.
That means that the upper and the lower layer are hard coupled in the code.
Due to this deadlocks can happen as seen below while transmitting data,
especially during ldisc congestion. Furthermore, the data channels starve
the control channel on high transmission load on the ldisc.
Introduce an additional control channel data queue to prevent timeouts and
link hangups during ldisc congestion. This is being processed before the
user channel data queue in gsm_data_kick(), i.e. with the highest priority.
Put the queue to ldisc data path into a tasklet and trigger it whenever new
data has been put into the transmission queue. Change gsm_dlci_data_sweep()
accordingly to fill up the transmission queue until TX_THRESH_HI. This
solves the locking issue, keeps latency low and provides good performance
on high data load.
Add an timer that kicks in after T1 to keep filling the transmission queue
if the link starts idling.
BUG: spinlock recursion on CPU#0, test_v24_loop/124
lock: serial8250_ports+0x3a8/0x7500, .magic: dead4ead, .owner: test_v24_loop/124, .owner_cpu: 0
CPU: 0 PID: 124 Comm: test_v24_loop Tainted: G O 5.18.0-rc2 #3
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
Call Trace:
<IRQ>
dump_stack_lvl+0x34/0x44
do_raw_spin_lock+0x76/0xa0
_raw_spin_lock_irqsave+0x72/0x80
uart_write_room+0x3b/0xc0
gsm_data_kick+0x14b/0x240 [n_gsm]
gsmld_write_wakeup+0x35/0x70 [n_gsm]
tty_wakeup+0x53/0x60
tty_port_default_wakeup+0x1b/0x30
serial8250_tx_chars+0x12f/0x220
serial8250_handle_irq.part.0+0xfe/0x150
serial8250_default_handle_irq+0x48/0x80
serial8250_interrupt+0x56/0xa0
__handle_irq_event_percpu+0x78/0x1f0
handle_irq_event+0x34/0x70
handle_fasteoi_irq+0x90/0x1e0
__common_interrupt+0x69/0x100
common_interrupt+0x48/0xc0
asm_common_interrupt+0x1e/0x40
RIP: 0010:__do_softirq+0x83/0x34e
Code: 2a 0a ff 0f b7 ed c7 44 24 10 0a 00 00 00 48 c7 c7 51 2a 64 82 e8 2d
e2 d5 ff 65 66 c7 05 83 af 1e 7e 00 00 fb b8 ff ff ff ff <49> c7 c2 40 61
80 82 0f bc c5 41 89 c4 41 83 c4 01 0f 84 e6 00 00
RSP: 0018:ffffc90000003f98 EFLAGS: 00000286
RAX: 00000000ffffffff RBX: 0000000000000000 RCX: 0000000000000000
RDX: 0000000000000000 RSI: ffffffff82642a51 RDI: ffffffff825bb5e7
RBP: 0000000000000200 R08: 00000008de3271a8 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000000
R13: 0000000000000030 R14: 0000000000000000 R15: 0000000000000000
? __do_softirq+0x73/0x34e
irq_exit_rcu+0xb5/0x100
common_interrupt+0xa4/0xc0
</IRQ>
<TASK>
asm_common_interrupt+0x1e/0x40
RIP: 0010:_raw_spin_unlock_irqrestore+0x2e/0x50
Code: 00 55 48 89 fd 48 83 c7 18 53 48 89 f3 48 8b 74 24 10 e8 85 28 36 ff
48 89 ef e8 cd 58 36 ff 80 e7 02 74 01 fb bf 01 00 00 00 <e8> 3d 97 33 ff
65 8b 05 96 23 2b 7e 85 c0 74 03 5b 5d c3 0f 1f 44
RSP: 0018:ffffc9000020fd08 EFLAGS: 00000202
RAX: 0000000000000000 RBX: 0000000000000246 RCX: 0000000000000000
RDX: 0000000000000004 RSI: ffffffff8257fd74 RDI: 0000000000000001
RBP: ffff8880057de3a0 R08: 00000008de233000 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000000
R13: 0000000000000100 R14: 0000000000000202 R15: ffff8880057df0b8
? _raw_spin_unlock_irqrestore+0x23/0x50
gsmtty_write+0x65/0x80 [n_gsm]
n_tty_write+0x33f/0x530
? swake_up_all+0xe0/0xe0
file_tty_write.constprop.0+0x1b1/0x320
? n_tty_flush_buffer+0xb0/0xb0
new_sync_write+0x10c/0x190
vfs_write+0x282/0x310
ksys_write+0x68/0xe0
do_syscall_64+0x3b/0x90
entry_SYSCALL_64_after_hwframe+0x44/0xae
RIP: 0033:0x7f3e5e35c15c
Code: 8b 7c 24 08 89 c5 e8 c5 ff ff ff 89 ef 89 44 24 08 e8 58 bc 02 00 8b
44 24 08 48 83 c4 10 5d c3 48 63 ff b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff
ff 76 10 48 8b 15 fd fc 05 00 f7 d8 64 89 02 48 83
RSP: 002b:00007ffcee77cd18 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007ffcee77cd70 RCX: 00007f3e5e35c15c
RDX: 0000000000000100 RSI: 00007ffcee77cd90 RDI: 0000000000000003
RBP: 0000000000000100 R08: 0000000000000000 R09: 7efefefefefefeff
R10: 00007f3e5e3bddeb R11: 0000000000000246 R12: 00007ffcee77ce8f
R13: 0000000000000001 R14: 000056214404e010 R15: 00007ffcee77cd90
</TASK>
Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
Cc: [email protected]
Signed-off-by: Daniel Starke <[email protected]>
---
drivers/tty/n_gsm.c | 701 ++++++++++++++++++++++++++++++--------------
1 file changed, 487 insertions(+), 214 deletions(-)
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 08fea3e7674d..b87c752c82f5 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -5,6 +5,14 @@
*
* * THIS IS A DEVELOPMENT SNAPSHOT IT IS NOT A FINAL RELEASE *
*
+ * Outgoing path:
+ * tty -> DLCI fifo -> scheduler -> GSM MUX data queue ---o-> ldisc
+ * control message -> GSM MUX control queue --´
+ *
+ * Incoming path:
+ * ldisc -> gsm_queue() -o--> tty
+ * `-> gsm_control_response()
+ *
* TO DO:
* Mostly done: ioctls for setting modes/timing
* Partly done: hooks so you can pull off frames to non tty devs
@@ -210,6 +218,9 @@ struct gsm_mux {
/* Events on the GSM channel */
wait_queue_head_t event;
+ /* ldisc write task */
+ struct tasklet_struct tx_tsk;
+
/* Bits for GSM mode decoding */
/* Framing Layer */
@@ -240,9 +251,11 @@ struct gsm_mux {
unsigned int tx_bytes; /* TX data outstanding */
#define TX_THRESH_HI 8192
#define TX_THRESH_LO 2048
- struct list_head tx_list; /* Pending data packets */
+ struct list_head tx0_list; /* Pending control packets */
+ struct list_head tx1_list; /* Pending data packets */
/* Control messages */
+ struct timer_list kick_timer; /* Kick TX queuing on timeout */
struct timer_list t2_timer; /* Retransmit timer for commands */
int cretries; /* Command retry counter */
struct gsm_control *pending_cmd;/* Our current pending command */
@@ -369,6 +382,8 @@ static const u8 gsm_fcs8[256] = {
static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len);
static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk);
+static void gsmld_write_trigger(struct gsm_mux *gsm);
+static void gsmld_write_task(unsigned long arg);
/**
* gsm_fcs_add - update FCS
@@ -419,6 +434,29 @@ static int gsm_read_ea(unsigned int *val, u8 c)
return c & EA;
}
+/**
+ * gsm_read_ea_val - read a value until EA
+ * @val: variable holding value
+ * @data: buffer of data
+ * @clen: length of buffer
+ *
+ * Processes an EA value. Updates the passed variable and
+ * returns the processed data length.
+ */
+static int gsm_read_ea_val(unsigned int *val, const u8 *data, int clen)
+{
+ int len;
+
+ for (len = 0; clen > 0; len++, clen--) {
+ if (gsm_read_ea(val, *data++)) {
+ /* done */
+ len += 1;
+ break;
+ }
+ }
+ return len;
+}
+
/**
* gsm_encode_modem - encode modem data bits
* @dlci: DLCI to encode from
@@ -544,94 +582,6 @@ static int gsm_stuff_frame(const u8 *input, u8 *output, int len)
return olen;
}
-/**
- * gsm_send - send a control frame
- * @gsm: our GSM mux
- * @addr: address for control frame
- * @cr: command/response bit seen as initiator
- * @control: control byte including PF bit
- *
- * Format up and transmit a control frame. These do not go via the
- * queueing logic as they should be transmitted ahead of data when
- * they are needed.
- *
- * FIXME: Lock versus data TX path
- */
-
-static void gsm_send(struct gsm_mux *gsm, int addr, int cr, int control)
-{
- int len;
- u8 cbuf[10];
- u8 ibuf[3];
- int ocr;
-
- /* toggle C/R coding if not initiator */
- ocr = cr ^ (gsm->initiator ? 0 : 1);
-
- switch (gsm->encoding) {
- case 0:
- cbuf[0] = GSM0_SOF;
- cbuf[1] = (addr << 2) | (ocr << 1) | EA;
- cbuf[2] = control;
- cbuf[3] = EA; /* Length of data = 0 */
- cbuf[4] = 0xFF - gsm_fcs_add_block(INIT_FCS, cbuf + 1, 3);
- cbuf[5] = GSM0_SOF;
- len = 6;
- break;
- case 1:
- case 2:
- /* Control frame + packing (but not frame stuffing) in mode 1 */
- ibuf[0] = (addr << 2) | (ocr << 1) | EA;
- ibuf[1] = control;
- ibuf[2] = 0xFF - gsm_fcs_add_block(INIT_FCS, ibuf, 2);
- /* Stuffing may double the size worst case */
- len = gsm_stuff_frame(ibuf, cbuf + 1, 3);
- /* Now add the SOF markers */
- cbuf[0] = GSM1_SOF;
- cbuf[len + 1] = GSM1_SOF;
- /* FIXME: we can omit the lead one in many cases */
- len += 2;
- break;
- default:
- WARN_ON(1);
- return;
- }
- gsmld_output(gsm, cbuf, len);
- if (!gsm->initiator) {
- cr = cr & gsm->initiator;
- control = control & ~PF;
- }
- gsm_print_packet("-->", addr, cr, control, NULL, 0);
-}
-
-/**
- * gsm_response - send a control response
- * @gsm: our GSM mux
- * @addr: address for control frame
- * @control: control byte including PF bit
- *
- * Format up and transmit a link level response frame.
- */
-
-static inline void gsm_response(struct gsm_mux *gsm, int addr, int control)
-{
- gsm_send(gsm, addr, 0, control);
-}
-
-/**
- * gsm_command - send a control command
- * @gsm: our GSM mux
- * @addr: address for control frame
- * @control: control byte including PF bit
- *
- * Format up and transmit a link level command frame.
- */
-
-static inline void gsm_command(struct gsm_mux *gsm, int addr, int control)
-{
- gsm_send(gsm, addr, 1, control);
-}
-
/* Data transmission */
#define HDR_LEN 6 /* ADDR CTRL [LEN.2] DATA FCS */
@@ -664,61 +614,158 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
}
/**
- * gsm_data_kick - poke the queue
+ * gsm_send_packet - sends a single packet
* @gsm: GSM Mux
- * @dlci: DLCI sending the data
+ * @msg: packet to send
*
- * The tty device has called us to indicate that room has appeared in
- * the transmit queue. Ram more data into the pipe if we have any
- * If we have been flow-stopped by a CMD_FCOFF, then we can only
- * send messages on DLCI0 until CMD_FCON
+ * The given packet is encoded and send out. No memory is freed.
+ * The caller must hold the gsm tx lock.
+ */
+static int gsm_send_packet(struct gsm_mux *gsm, struct gsm_msg *msg)
+{
+ int len, ret;
+
+
+ if (gsm->encoding == 0) {
+ gsm->txframe[0] = GSM0_SOF;
+ memcpy(gsm->txframe + 1, msg->data, msg->len);
+ gsm->txframe[msg->len + 1] = GSM0_SOF;
+ len = msg->len + 2;
+ } else {
+ gsm->txframe[0] = GSM1_SOF;
+ len = gsm_stuff_frame(msg->data, gsm->txframe + 1, msg->len);
+ gsm->txframe[len + 1] = GSM1_SOF;
+ len += 2;
+ }
+
+ if (debug & 4)
+ print_hex_dump_bytes("gsm_send_packet: ", DUMP_PREFIX_OFFSET,
+ gsm->txframe, len);
+ gsm_print_packet("-->", msg->addr, gsm->initiator, msg->ctrl, msg->data,
+ msg->len);
+
+ ret = gsmld_output(gsm, gsm->txframe, len);
+ if (ret <= 0)
+ return ret;
+ gsm->tx_bytes -= msg->len;
+
+ return 0;
+}
+
+/**
+ * gsm_is_ctrl_flow_msg - checks if control flow message
+ * @msg: message to check
*
- * FIXME: lock against link layer control transmissions
+ * Returns non zero if the given message is a flow control command of the
+ * control channel. Zero is returned in any other case.
*/
+static int gsm_is_ctrl_flow_msg(struct gsm_msg *msg)
+{
+ int ret;
+ unsigned int cmd;
+
+ if (msg->addr > 0)
+ return 0;
+
+ ret = 0;
+ switch (msg->ctrl & ~PF) {
+ case UI:
+ case UIH:
+ cmd = 0;
+ if (gsm_read_ea_val(&cmd, msg->data + 2, msg->len - 2) < 1)
+ break;
+ switch (cmd & ~PF) {
+ case CMD_FCOFF:
+ case CMD_FCON:
+ ret = 1;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
-static void gsm_data_kick(struct gsm_mux *gsm, struct gsm_dlci *dlci)
+/**
+ * gsm_data_kick - poke the queue
+ * @gsm: GSM Mux
+ *
+ * The tty device has called us to indicate that room has appeared in
+ * the transmit queue. Ram more data into the pipe if we have any.
+ * If we have been flow-stopped by a CMD_FCOFF, then we can only
+ * send messages on DLCI0 until CMD_FCON. The caller must hold
+ * the gsm tx lock.
+ */
+static int gsm_data_kick(struct gsm_mux *gsm)
{
struct gsm_msg *msg, *nmsg;
- int len;
+ struct gsm_dlci *dlci;
+ int ret;
- list_for_each_entry_safe(msg, nmsg, &gsm->tx_list, list) {
- if (gsm->constipated && msg->addr)
+ clear_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags);
+
+ /* Serialize control messages and control channel messages first */
+ list_for_each_entry_safe(msg, nmsg, &gsm->tx0_list, list) {
+ if (gsm->constipated && !gsm_is_ctrl_flow_msg(msg))
+ return -EAGAIN;
+ ret = gsm_send_packet(gsm, msg);
+ switch (ret) {
+ case -ENOSPC:
+ return -ENOSPC;
+ case -ENODEV:
+ /* ldisc not open */
+ gsm->tx_bytes -= msg->len;
+ list_del(&msg->list);
+ kfree(msg);
continue;
- if (gsm->encoding != 0) {
- gsm->txframe[0] = GSM1_SOF;
- len = gsm_stuff_frame(msg->data,
- gsm->txframe + 1, msg->len);
- gsm->txframe[len + 1] = GSM1_SOF;
- len += 2;
- } else {
- gsm->txframe[0] = GSM0_SOF;
- memcpy(gsm->txframe + 1 , msg->data, msg->len);
- gsm->txframe[msg->len + 1] = GSM0_SOF;
- len = msg->len + 2;
- }
-
- if (debug & 4)
- print_hex_dump_bytes("gsm_data_kick: ",
- DUMP_PREFIX_OFFSET,
- gsm->txframe, len);
- if (gsmld_output(gsm, gsm->txframe, len) <= 0)
+ default:
+ if (ret >= 0) {
+ list_del(&msg->list);
+ kfree(msg);
+ }
break;
- /* FIXME: Can eliminate one SOF in many more cases */
- gsm->tx_bytes -= msg->len;
-
- list_del(&msg->list);
- kfree(msg);
+ }
+ }
- if (dlci) {
- tty_port_tty_wakeup(&dlci->port);
- } else {
- int i = 0;
+ if (gsm->constipated)
+ return -EAGAIN;
- for (i = 0; i < NUM_DLCI; i++)
- if (gsm->dlci[i])
- tty_port_tty_wakeup(&gsm->dlci[i]->port);
+ /* Serialize other channels */
+ if (list_empty(&gsm->tx1_list))
+ return 0;
+ list_for_each_entry_safe(msg, nmsg, &gsm->tx1_list, list) {
+ dlci = gsm->dlci[msg->addr];
+ /* Send only messages for DLCIs with valid state */
+ if (dlci->state != DLCI_OPEN) {
+ gsm->tx_bytes -= msg->len;
+ list_del(&msg->list);
+ kfree(msg);
+ continue;
+ }
+ ret = gsm_send_packet(gsm, msg);
+ switch (ret) {
+ case -ENOSPC:
+ return -ENOSPC;
+ case -ENODEV:
+ /* ldisc not open */
+ gsm->tx_bytes -= msg->len;
+ list_del(&msg->list);
+ kfree(msg);
+ continue;
+ default:
+ if (ret >= 0) {
+ list_del(&msg->list);
+ kfree(msg);
+ }
+ break;
}
}
+
+ return 1;
}
/**
@@ -767,9 +814,22 @@ static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg)
msg->data = dp;
/* Add to the actual output queue */
- list_add_tail(&msg->list, &gsm->tx_list);
+ switch (msg->ctrl & ~PF) {
+ case UI:
+ case UIH:
+ if (msg->addr > 0)
+ list_add_tail(&msg->list, &gsm->tx1_list);
+ else
+ list_add_tail(&msg->list, &gsm->tx0_list);
+ break;
+ default:
+ list_add_tail(&msg->list, &gsm->tx0_list);
+ break;
+ }
gsm->tx_bytes += msg->len;
- gsm_data_kick(gsm, dlci);
+
+ gsmld_write_trigger(gsm);
+ mod_timer(&gsm->kick_timer, jiffies + 10 * gsm->t1 * HZ / 100);
}
/**
@@ -790,6 +850,112 @@ static void gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg)
spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags);
}
+/**
+ * gsm_send - send a control frame
+ * @gsm: our GSM mux
+ * @addr: address for control frame
+ * @cr: command/response bit seen as initiator
+ * @control: control byte including PF bit
+ *
+ * Format up and transmit a control frame. These should be transmitted
+ * ahead of data when they are needed.
+ */
+static int gsm_send(struct gsm_mux *gsm, int addr, int cr, int control)
+{
+ struct gsm_msg *msg;
+ u8 *dp;
+ int ocr;
+ unsigned long flags;
+
+ msg = gsm_data_alloc(gsm, addr, 0, control);
+ if (!msg) {
+ pr_err("%s: gsm_data_alloc error", __func__);
+ return -ENOMEM;
+ }
+
+ /* toggle C/R coding if not initiator */
+ ocr = cr ^ (gsm->initiator ? 0 : 1);
+
+ msg->data -= 3;
+ dp = msg->data;
+ *dp++ = (addr << 2) | (ocr << 1) | EA;
+ *dp++ = control;
+
+ if (gsm->encoding == 0)
+ *dp++ = EA; /* Length of data = 0 */
+
+ *dp = 0xFF - gsm_fcs_add_block(INIT_FCS, msg->data, dp - msg->data);
+ msg->len = (dp - msg->data) + 1;
+
+ gsm_print_packet("Q->", addr, cr, control, NULL, 0);
+
+ spin_lock_irqsave(&gsm->tx_lock, flags);
+ list_add_tail(&msg->list, &gsm->tx0_list);
+ gsm->tx_bytes += msg->len;
+ spin_unlock_irqrestore(&gsm->tx_lock, flags);
+ gsmld_write_trigger(gsm);
+
+ return 0;
+}
+
+/**
+ * gsm_response - send a control response
+ * @gsm: our GSM mux
+ * @addr: address for control frame
+ * @control: control byte including PF bit
+ *
+ * Format up and transmit a link level response frame.
+ */
+
+static inline void gsm_response(struct gsm_mux *gsm, int addr, int control)
+{
+ gsm_send(gsm, addr, 0, control);
+}
+
+/**
+ * gsm_command - send a control command
+ * @gsm: our GSM mux
+ * @addr: address for control frame
+ * @control: control byte including PF bit
+ *
+ * Format up and transmit a link level command frame.
+ */
+
+static inline void gsm_command(struct gsm_mux *gsm, int addr, int control)
+{
+ gsm_send(gsm, addr, 1, control);
+}
+
+/**
+ * gsm_dlci_clear_queues - remove outstanding data for a DLCI
+ * @gsm: mux
+ * @dlci: clear for this DLCI
+ *
+ * Clears the data queues for a given DLCI.
+ */
+static void gsm_dlci_clear_queues(struct gsm_mux *gsm, struct gsm_dlci *dlci)
+{
+ struct gsm_msg *msg, *nmsg;
+ int addr = dlci->addr;
+ unsigned long flags;
+
+ /* Clear DLCI write fifo first */
+ spin_lock_irqsave(&dlci->lock, flags);
+ kfifo_reset(&dlci->fifo);
+ spin_unlock_irqrestore(&dlci->lock, flags);
+
+ /* Clear data packets in MUX write queue */
+ spin_lock_irqsave(&gsm->tx_lock, flags);
+ list_for_each_entry_safe(msg, nmsg, &gsm->tx1_list, list) {
+ if (msg->addr != addr)
+ continue;
+ gsm->tx_bytes -= msg->len;
+ list_del(&msg->list);
+ kfree(msg);
+ }
+ spin_unlock_irqrestore(&gsm->tx_lock, flags);
+}
+
/**
* gsm_dlci_data_output - try and push data out of a DLCI
* @gsm: mux
@@ -804,43 +970,52 @@ static void gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg)
static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
{
+ u8 *dp = NULL;
struct gsm_msg *msg;
- u8 *dp;
- int len, total_size, size;
- int h = dlci->adaption - 1;
+ int h, len, size;
- total_size = 0;
- while (1) {
- len = kfifo_len(&dlci->fifo);
- if (len == 0)
- return total_size;
-
- /* MTU/MRU count only the data bits */
- if (len > gsm->mtu)
- len = gsm->mtu;
-
- size = len + h;
-
- msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
- /* FIXME: need a timer or something to kick this so it can't
- get stuck with no work outstanding and no buffer free */
- if (msg == NULL)
- return -ENOMEM;
- dp = msg->data;
- switch (dlci->adaption) {
- case 1: /* Unstructured */
- break;
- case 2: /* Unstructed with modem bits.
- Always one byte as we never send inline break data */
- *dp++ = (gsm_encode_modem(dlci) << 1) | EA;
- break;
- }
- WARN_ON(kfifo_out_locked(&dlci->fifo, dp , len, &dlci->lock) != len);
- __gsm_data_queue(dlci, msg);
- total_size += size;
+ /* for modem bits without break data */
+ h = ((dlci->adaption == 1) ? 0 : 1);
+
+ len = kfifo_len(&dlci->fifo);
+ if (len == 0)
+ return 0;
+
+ /* MTU/MRU count only the data bits but watch adaption mode */
+ if ((len + h) > gsm->mtu)
+ len = gsm->mtu - h;
+
+ size = len + h;
+
+ msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
+ if (!msg) {
+ pr_err("%s: gsm_data_alloc error", __func__);
+ return -ENOMEM;
}
+ dp = msg->data;
+ switch (dlci->adaption) {
+ case 1: /* Unstructured */
+ break;
+ case 2: /* Unstructured with modem bits.
+ * Always one byte as we never send inline break data
+ */
+ *dp++ = (gsm_encode_modem(dlci) << 1) | EA;
+ break;
+ default:
+ pr_err("%s: unsupported adaption %d\n", __func__,
+ dlci->adaption);
+ break;
+ }
+
+ WARN_ON(len != kfifo_out_locked(&dlci->fifo, dp, len,
+ &dlci->lock));
+
+ /* Notify upper layer about available send space. */
+ tty_port_tty_wakeup(&dlci->port);
+
+ __gsm_data_queue(dlci, msg);
/* Bytes of data we used up */
- return total_size;
+ return size;
}
/**
@@ -989,32 +1164,43 @@ static int gsm_dlci_modem_output(struct gsm_mux *gsm, struct gsm_dlci *dlci,
* renegotiate DLCI priorities with optional stuff. Needs optimising.
*/
-static void gsm_dlci_data_sweep(struct gsm_mux *gsm)
+static int gsm_dlci_data_sweep(struct gsm_mux *gsm)
{
- int len;
/* Priority ordering: We should do priority with RR of the groups */
- int i = 1;
-
- while (i < NUM_DLCI) {
- struct gsm_dlci *dlci;
+ int i, len, ret = 0;
+ bool sent;
+ struct gsm_dlci *dlci;
- if (gsm->tx_bytes > TX_THRESH_HI)
- break;
- dlci = gsm->dlci[i];
- if (dlci == NULL || dlci->constipated) {
- i++;
- continue;
+ while (gsm->tx_bytes < TX_THRESH_HI) {
+ for (sent = false, i = 1; i < NUM_DLCI; i++) {
+ dlci = gsm->dlci[i];
+ /* skip unused or blocked channel */
+ if (!dlci || dlci->constipated)
+ continue;
+ /* skip channels with invalid state */
+ if (dlci->state != DLCI_OPEN)
+ continue;
+ /* count the sent data per adaption */
+ if (dlci->adaption < 3 && !dlci->net)
+ len = gsm_dlci_data_output(gsm, dlci);
+ else
+ len = gsm_dlci_data_output_framed(gsm, dlci);
+ /* on error exit */
+ if (len < 0)
+ return ret;
+ if (len > 0) {
+ ret++;
+ sent = true;
+ /* The lower DLCs can starve the higher DLCs! */
+ break;
+ }
+ /* try next */
}
- if (dlci->adaption < 3 && !dlci->net)
- len = gsm_dlci_data_output(gsm, dlci);
- else
- len = gsm_dlci_data_output_framed(gsm, dlci);
- if (len < 0)
+ if (!sent)
break;
- /* DLCI empty - try the next */
- if (len == 0)
- i++;
- }
+ };
+
+ return ret;
}
/**
@@ -1260,7 +1446,6 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
const u8 *data, int clen)
{
u8 buf[1];
- unsigned long flags;
switch (command) {
case CMD_CLD: {
@@ -1282,9 +1467,7 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
gsm->constipated = false;
gsm_control_reply(gsm, CMD_FCON, NULL, 0);
/* Kick the link in case it is idling */
- spin_lock_irqsave(&gsm->tx_lock, flags);
- gsm_data_kick(gsm, NULL);
- spin_unlock_irqrestore(&gsm->tx_lock, flags);
+ gsmld_write_trigger(gsm);
break;
case CMD_FCOFF:
/* Modem wants us to STFU */
@@ -1487,7 +1670,7 @@ static int gsm_control_wait(struct gsm_mux *gsm, struct gsm_control *control)
static void gsm_dlci_close(struct gsm_dlci *dlci)
{
- unsigned long flags;
+ struct gsm_mux *gsm = dlci->gsm;
del_timer(&dlci->t1);
if (debug & 8)
@@ -1497,9 +1680,7 @@ static void gsm_dlci_close(struct gsm_dlci *dlci)
dlci->constipated = true;
if (dlci->addr != 0) {
tty_port_tty_hangup(&dlci->port, false);
- spin_lock_irqsave(&dlci->lock, flags);
- kfifo_reset(&dlci->fifo);
- spin_unlock_irqrestore(&dlci->lock, flags);
+ gsm_dlci_clear_queues(gsm, dlci);
/* Ensure that gsmtty_open() can return. */
tty_port_set_initialized(&dlci->port, 0);
wake_up_interruptible(&dlci->port.open_wait);
@@ -1530,6 +1711,7 @@ static void gsm_dlci_open(struct gsm_dlci *dlci)
/* Send current modem state */
if (dlci->addr)
gsm_modem_update(dlci, 0);
+ gsm_dlci_data_kick(dlci);
wake_up(&dlci->gsm->event);
}
@@ -1733,6 +1915,30 @@ static void gsm_dlci_command(struct gsm_dlci *dlci, const u8 *data, int len)
}
}
+/**
+ * gsm_kick_timer - transmit if possible
+ * @t: timer contained in our gsm object
+ *
+ * Transmit data from DLCIs if the queue is empty. We can't rely on
+ * a tty wakeup except when we filled the pipe so we need to fire off
+ * new data ourselves in other cases.
+ */
+static void gsm_kick_timer(struct timer_list *t)
+{
+ struct gsm_mux *gsm = from_timer(gsm, t, kick_timer);
+ unsigned long flags;
+ int sent = 0;
+
+ spin_lock_irqsave(&gsm->tx_lock, flags);
+ /* If we have nothing running then we need to fire up */
+ if (gsm->tx_bytes < TX_THRESH_LO)
+ sent = gsm_dlci_data_sweep(gsm);
+ spin_unlock_irqrestore(&gsm->tx_lock, flags);
+
+ if (sent && debug & 4)
+ pr_info("%s TX queue stalled\n", __func__);
+}
+
/*
* Allocate/Free DLCI channels
*/
@@ -2060,7 +2266,7 @@ static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
} else if ((c & ISO_IEC_646_MASK) == XOFF) {
gsm->constipated = false;
/* Kick the link in case it is idling */
- gsm_data_kick(gsm, NULL);
+ gsmld_write_trigger(gsm);
return;
}
if (c == GSM1_SOF) {
@@ -2188,8 +2394,13 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
}
/* Finish outstanding timers, making sure they are done */
+ del_timer_sync(&gsm->kick_timer);
del_timer_sync(&gsm->t2_timer);
+ /* Finish writing task */
+ tasklet_disable(&gsm->tx_tsk);
+ tasklet_kill(&gsm->tx_tsk);
+
/* Free up any link layer users and finally the control channel */
for (i = NUM_DLCI - 1; i >= 0; i--)
if (gsm->dlci[i])
@@ -2197,9 +2408,12 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
mutex_unlock(&gsm->mutex);
/* Now wipe the queues */
tty_ldisc_flush(gsm->tty);
- list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
+ list_for_each_entry_safe(txq, ntxq, &gsm->tx0_list, list)
+ kfree(txq);
+ INIT_LIST_HEAD(&gsm->tx0_list);
+ list_for_each_entry_safe(txq, ntxq, &gsm->tx1_list, list)
kfree(txq);
- INIT_LIST_HEAD(&gsm->tx_list);
+ INIT_LIST_HEAD(&gsm->tx1_list);
}
/**
@@ -2215,10 +2429,9 @@ static int gsm_activate_mux(struct gsm_mux *gsm)
{
struct gsm_dlci *dlci;
+ timer_setup(&gsm->kick_timer, gsm_kick_timer, 0);
timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0);
- init_waitqueue_head(&gsm->event);
- spin_lock_init(&gsm->control_lock);
- spin_lock_init(&gsm->tx_lock);
+ tasklet_init(&gsm->tx_tsk, gsmld_write_task, (unsigned long)gsm);
if (gsm->encoding == 0)
gsm->receive = gsm0_receive;
@@ -2317,10 +2530,15 @@ static struct gsm_mux *gsm_alloc_mux(void)
kfree(gsm);
return NULL;
}
+ /* Initialize locks and queues */
+ init_waitqueue_head(&gsm->event);
+ spin_lock_init(&gsm->control_lock);
+ spin_lock_init(&gsm->tx_lock);
spin_lock_init(&gsm->lock);
mutex_init(&gsm->mutex);
kref_init(&gsm->ref);
- INIT_LIST_HEAD(&gsm->tx_list);
+ INIT_LIST_HEAD(&gsm->tx0_list);
+ INIT_LIST_HEAD(&gsm->tx1_list);
gsm->t1 = T1;
gsm->t2 = T2;
@@ -2478,6 +2696,48 @@ static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len)
return gsm->tty->ops->write(gsm->tty, data, len);
}
+
+/**
+ * gsmld_write_trigger - schedule ldisc write task
+ * @gsm: our mux
+ */
+static void gsmld_write_trigger(struct gsm_mux *gsm)
+{
+ if (!gsm || !gsm->dlci[0] || gsm->dlci[0]->dead)
+ return;
+ tasklet_schedule(&gsm->tx_tsk);
+}
+
+
+/**
+ * gsmld_write_task - ldisc write task
+ * @arg: our mux
+ *
+ * Writes out data to the ldisc if possible. We are doing this in a task
+ * to avoid dead-locking. This task returns if no space or data is left for
+ * output.
+ */
+static void gsmld_write_task(unsigned long arg)
+{
+ struct gsm_mux *gsm = (struct gsm_mux *)arg;
+ unsigned long flags;
+ int i, ret;
+
+ /* All outstanding control channel and control messages and one data
+ * frame is sent.
+ */
+ ret = -ENODEV;
+ spin_lock_irqsave(&gsm->tx_lock, flags);
+ if (gsm->tty)
+ ret = gsm_data_kick(gsm);
+ spin_unlock_irqrestore(&gsm->tx_lock, flags);
+
+ if (ret >= 0)
+ for (i = 0; i < NUM_DLCI; i++)
+ if (gsm->dlci[i])
+ tty_port_tty_wakeup(&gsm->dlci[i]->port);
+}
+
/**
* gsmld_attach_gsm - mode set up
* @tty: our tty structure
@@ -2602,6 +2862,7 @@ static void gsmld_flush_buffer(struct tty_struct *tty)
static void gsmld_close(struct tty_struct *tty)
{
struct gsm_mux *gsm = tty->disc_data;
+ unsigned long tx_flags;
/* The ldisc locks and closes the port before calling our close. This
* means we have no way to do a proper disconnect. We will not bother
@@ -2609,7 +2870,10 @@ static void gsmld_close(struct tty_struct *tty)
*/
gsm_cleanup_mux(gsm, false);
+ /* Prevent write during detach */
+ spin_lock_irqsave(&gsm->tx_lock, tx_flags);
gsmld_detach_gsm(tty, gsm);
+ spin_unlock_irqrestore(&gsm->tx_lock, tx_flags);
gsmld_flush_buffer(tty);
/* Do other clean up here */
@@ -2665,16 +2929,12 @@ static int gsmld_open(struct tty_struct *tty)
static void gsmld_write_wakeup(struct tty_struct *tty)
{
struct gsm_mux *gsm = tty->disc_data;
- unsigned long flags;
+
+ if (!gsm)
+ return;
/* Queue poll */
- clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
- spin_lock_irqsave(&gsm->tx_lock, flags);
- gsm_data_kick(gsm, NULL);
- if (gsm->tx_bytes < TX_THRESH_LO) {
- gsm_dlci_data_sweep(gsm);
- }
- spin_unlock_irqrestore(&gsm->tx_lock, flags);
+ gsmld_write_trigger(gsm);
}
/**
@@ -2718,11 +2978,24 @@ static ssize_t gsmld_read(struct tty_struct *tty, struct file *file,
static ssize_t gsmld_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr)
{
- int space = tty_write_room(tty);
- if (space >= nr)
- return tty->ops->write(tty, buf, nr);
- set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
- return -ENOBUFS;
+ int space;
+ int ret;
+ struct gsm_mux *gsm = tty->disc_data;
+ unsigned long flags;
+
+ if (!gsm)
+ return -ENODEV;
+
+ space = tty_write_room(tty);
+ if (space >= nr) {
+ spin_lock_irqsave(&gsm->tx_lock, flags);
+ ret = tty->ops->write(tty, buf, nr);
+ spin_unlock_irqrestore(&gsm->tx_lock, flags);
+ } else {
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ ret = -ENOBUFS;
+ }
+ return ret;
}
/**
--
2.34.1
On 06. 05. 22, 16:47, D. Starke wrote:
> From: Daniel Starke <[email protected]>
>
> The current implementation queues up new control and user packets as needed
> and processes this queue down to the ldisc in the same code path.
> That means that the upper and the lower layer are hard coupled in the code.
> Due to this deadlocks can happen as seen below while transmitting data,
> especially during ldisc congestion. Furthermore, the data channels starve
> the control channel on high transmission load on the ldisc.
>
> Introduce an additional control channel data queue to prevent timeouts and
> link hangups during ldisc congestion. This is being processed before the
> user channel data queue in gsm_data_kick(), i.e. with the highest priority.
> Put the queue to ldisc data path into a tasklet and trigger it whenever new
Tasklet? There is an ongoing work to phase them all out. So please don't
add a new one -- you'd have to use something different.
...
> --- a/drivers/tty/n_gsm.c
> +++ b/drivers/tty/n_gsm.c
> @@ -5,6 +5,14 @@
> *
> * * THIS IS A DEVELOPMENT SNAPSHOT IT IS NOT A FINAL RELEASE *
> *
> + * Outgoing path:
> + * tty -> DLCI fifo -> scheduler -> GSM MUX data queue ---o-> ldisc
> + * control message -> GSM MUX control queue --´
> + *
> + * Incoming path:
> + * ldisc -> gsm_queue() -o--> tty
> + * `-> gsm_control_response()
> + *
> * TO DO:
> * Mostly done: ioctls for setting modes/timing
> * Partly done: hooks so you can pull off frames to non tty devs
> @@ -210,6 +218,9 @@ struct gsm_mux {
> /* Events on the GSM channel */
> wait_queue_head_t event;
>
> + /* ldisc write task */
> + struct tasklet_struct tx_tsk;
> +
> /* Bits for GSM mode decoding */
>
> /* Framing Layer */
> @@ -240,9 +251,11 @@ struct gsm_mux {
> unsigned int tx_bytes; /* TX data outstanding */
> #define TX_THRESH_HI 8192
> #define TX_THRESH_LO 2048
> - struct list_head tx_list; /* Pending data packets */
> + struct list_head tx0_list; /* Pending control packets */
> + struct list_head tx1_list; /* Pending data packets */
>
> /* Control messages */
> + struct timer_list kick_timer; /* Kick TX queuing on timeout */
> struct timer_list t2_timer; /* Retransmit timer for commands */
> int cretries; /* Command retry counter */
> struct gsm_control *pending_cmd;/* Our current pending command */
> @@ -369,6 +382,8 @@ static const u8 gsm_fcs8[256] = {
>
> static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len);
> static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk);
> +static void gsmld_write_trigger(struct gsm_mux *gsm);
> +static void gsmld_write_task(unsigned long arg);
>
> /**
> * gsm_fcs_add - update FCS
> @@ -419,6 +434,29 @@ static int gsm_read_ea(unsigned int *val, u8 c)
> return c & EA;
> }
>
> +/**
> + * gsm_read_ea_val - read a value until EA
> + * @val: variable holding value
> + * @data: buffer of data
> + * @clen: length of buffer
> + *
> + * Processes an EA value. Updates the passed variable and
> + * returns the processed data length.
> + */
> +static int gsm_read_ea_val(unsigned int *val, const u8 *data, int clen)
So clen can be negative provided it is (signed) int?
> +{
> + int len;
> +
> + for (len = 0; clen > 0; len++, clen--) {
> + if (gsm_read_ea(val, *data++)) {
> + /* done */
> + len += 1;
len++
> + break;
> + }
> + }
Anyway, is that a harder-to-read variant of:
unsigned int len = 0;
for (; clen > 0; clen--) {
len++;
if (gsm_read_ea(val, *data++))
break;
}
?
> + return len;
> +}
> +
> /**
> * gsm_encode_modem - encode modem data bits
> * @dlci: DLCI to encode from
> @@ -544,94 +582,6 @@ static int gsm_stuff_frame(const u8 *input, u8 *output, int len)
> return olen;
> }
>
> -/**
> - * gsm_send - send a control frame
> - * @gsm: our GSM mux
> - * @addr: address for control frame
> - * @cr: command/response bit seen as initiator
> - * @control: control byte including PF bit
> - *
> - * Format up and transmit a control frame. These do not go via the
> - * queueing logic as they should be transmitted ahead of data when
> - * they are needed.
> - *
> - * FIXME: Lock versus data TX path
> - */
> -
> -static void gsm_send(struct gsm_mux *gsm, int addr, int cr, int control)
> -{
> - int len;
> - u8 cbuf[10];
> - u8 ibuf[3];
> - int ocr;
> -
> - /* toggle C/R coding if not initiator */
> - ocr = cr ^ (gsm->initiator ? 0 : 1);
> -
> - switch (gsm->encoding) {
> - case 0:
> - cbuf[0] = GSM0_SOF;
> - cbuf[1] = (addr << 2) | (ocr << 1) | EA;
> - cbuf[2] = control;
> - cbuf[3] = EA; /* Length of data = 0 */
> - cbuf[4] = 0xFF - gsm_fcs_add_block(INIT_FCS, cbuf + 1, 3);
> - cbuf[5] = GSM0_SOF;
> - len = 6;
> - break;
> - case 1:
> - case 2:
> - /* Control frame + packing (but not frame stuffing) in mode 1 */
> - ibuf[0] = (addr << 2) | (ocr << 1) | EA;
> - ibuf[1] = control;
> - ibuf[2] = 0xFF - gsm_fcs_add_block(INIT_FCS, ibuf, 2);
> - /* Stuffing may double the size worst case */
> - len = gsm_stuff_frame(ibuf, cbuf + 1, 3);
> - /* Now add the SOF markers */
> - cbuf[0] = GSM1_SOF;
> - cbuf[len + 1] = GSM1_SOF;
> - /* FIXME: we can omit the lead one in many cases */
> - len += 2;
> - break;
> - default:
> - WARN_ON(1);
> - return;
> - }
> - gsmld_output(gsm, cbuf, len);
> - if (!gsm->initiator) {
> - cr = cr & gsm->initiator;
> - control = control & ~PF;
> - }
> - gsm_print_packet("-->", addr, cr, control, NULL, 0);
> -}
> -
> -/**
> - * gsm_response - send a control response
> - * @gsm: our GSM mux
> - * @addr: address for control frame
> - * @control: control byte including PF bit
> - *
> - * Format up and transmit a link level response frame.
> - */
> -
> -static inline void gsm_response(struct gsm_mux *gsm, int addr, int control)
> -{
> - gsm_send(gsm, addr, 0, control);
> -}
> -
> -/**
> - * gsm_command - send a control command
> - * @gsm: our GSM mux
> - * @addr: address for control frame
> - * @control: control byte including PF bit
> - *
> - * Format up and transmit a link level command frame.
> - */
> -
> -static inline void gsm_command(struct gsm_mux *gsm, int addr, int control)
> -{
> - gsm_send(gsm, addr, 1, control);
> -}
> -
> /* Data transmission */
>
> #define HDR_LEN 6 /* ADDR CTRL [LEN.2] DATA FCS */
> @@ -664,61 +614,158 @@ static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
> }
>
> /**
> - * gsm_data_kick - poke the queue
> + * gsm_send_packet - sends a single packet
> * @gsm: GSM Mux
> - * @dlci: DLCI sending the data
> + * @msg: packet to send
> *
> - * The tty device has called us to indicate that room has appeared in
> - * the transmit queue. Ram more data into the pipe if we have any
> - * If we have been flow-stopped by a CMD_FCOFF, then we can only
> - * send messages on DLCI0 until CMD_FCON
> + * The given packet is encoded and send out. No memory is freed.
> + * The caller must hold the gsm tx lock.
> + */
> +static int gsm_send_packet(struct gsm_mux *gsm, struct gsm_msg *msg)
> +{
> + int len, ret;
> +
> +
> + if (gsm->encoding == 0) {
> + gsm->txframe[0] = GSM0_SOF;
> + memcpy(gsm->txframe + 1, msg->data, msg->len);
> + gsm->txframe[msg->len + 1] = GSM0_SOF;
> + len = msg->len + 2;
> + } else {
> + gsm->txframe[0] = GSM1_SOF;
> + len = gsm_stuff_frame(msg->data, gsm->txframe + 1, msg->len);
> + gsm->txframe[len + 1] = GSM1_SOF;
> + len += 2;
> + }
> +
> + if (debug & 4)
> + print_hex_dump_bytes("gsm_send_packet: ", DUMP_PREFIX_OFFSET,
> + gsm->txframe, len);
> + gsm_print_packet("-->", msg->addr, gsm->initiator, msg->ctrl, msg->data,
> + msg->len);
> +
> + ret = gsmld_output(gsm, gsm->txframe, len);
> + if (ret <= 0)
> + return ret;
> + gsm->tx_bytes -= msg->len;
> +
> + return 0;
> +}
> +
> +/**
> + * gsm_is_ctrl_flow_msg - checks if control flow message
> + * @msg: message to check
> *
> - * FIXME: lock against link layer control transmissions
> + * Returns non zero if the given message is a flow control command of the
> + * control channel. Zero is returned in any other case.
> */
> +static int gsm_is_ctrl_flow_msg(struct gsm_msg *msg)
> +{
> + int ret;
> + unsigned int cmd;
> +
> + if (msg->addr > 0)
> + return 0;
> +
> + ret = 0;
> + switch (msg->ctrl & ~PF) {
> + case UI:
> + case UIH:
> + cmd = 0;
> + if (gsm_read_ea_val(&cmd, msg->data + 2, msg->len - 2) < 1)
> + break;
> + switch (cmd & ~PF) {
> + case CMD_FCOFF:
> + case CMD_FCON:
> + ret = 1;
> + break;
> + default:
> + break;
> + }
> + break;
> + default:
> + break;
> + }
> +
> + return ret;
> +}
>
> -static void gsm_data_kick(struct gsm_mux *gsm, struct gsm_dlci *dlci)
> +/**
> + * gsm_data_kick - poke the queue
> + * @gsm: GSM Mux
> + *
> + * The tty device has called us to indicate that room has appeared in
> + * the transmit queue. Ram more data into the pipe if we have any.
> + * If we have been flow-stopped by a CMD_FCOFF, then we can only
> + * send messages on DLCI0 until CMD_FCON. The caller must hold
> + * the gsm tx lock.
> + */
> +static int gsm_data_kick(struct gsm_mux *gsm)
> {
> struct gsm_msg *msg, *nmsg;
> - int len;
> + struct gsm_dlci *dlci;
> + int ret;
>
> - list_for_each_entry_safe(msg, nmsg, &gsm->tx_list, list) {
> - if (gsm->constipated && msg->addr)
> + clear_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags);
> +
> + /* Serialize control messages and control channel messages first */
> + list_for_each_entry_safe(msg, nmsg, &gsm->tx0_list, list) {
> + if (gsm->constipated && !gsm_is_ctrl_flow_msg(msg))
> + return -EAGAIN;
> + ret = gsm_send_packet(gsm, msg);
> + switch (ret) {
> + case -ENOSPC:
> + return -ENOSPC;
> + case -ENODEV:
> + /* ldisc not open */
> + gsm->tx_bytes -= msg->len;
> + list_del(&msg->list);
> + kfree(msg);
> continue;
> - if (gsm->encoding != 0) {
> - gsm->txframe[0] = GSM1_SOF;
> - len = gsm_stuff_frame(msg->data,
> - gsm->txframe + 1, msg->len);
> - gsm->txframe[len + 1] = GSM1_SOF;
> - len += 2;
> - } else {
> - gsm->txframe[0] = GSM0_SOF;
> - memcpy(gsm->txframe + 1 , msg->data, msg->len);
> - gsm->txframe[msg->len + 1] = GSM0_SOF;
> - len = msg->len + 2;
> - }
> -
> - if (debug & 4)
> - print_hex_dump_bytes("gsm_data_kick: ",
> - DUMP_PREFIX_OFFSET,
> - gsm->txframe, len);
> - if (gsmld_output(gsm, gsm->txframe, len) <= 0)
> + default:
> + if (ret >= 0) {
> + list_del(&msg->list);
> + kfree(msg);
> + }
> break;
> - /* FIXME: Can eliminate one SOF in many more cases */
> - gsm->tx_bytes -= msg->len;
> -
> - list_del(&msg->list);
> - kfree(msg);
> + }
> + }
>
> - if (dlci) {
> - tty_port_tty_wakeup(&dlci->port);
> - } else {
> - int i = 0;
> + if (gsm->constipated)
> + return -EAGAIN;
>
> - for (i = 0; i < NUM_DLCI; i++)
> - if (gsm->dlci[i])
> - tty_port_tty_wakeup(&gsm->dlci[i]->port);
> + /* Serialize other channels */
> + if (list_empty(&gsm->tx1_list))
> + return 0;
> + list_for_each_entry_safe(msg, nmsg, &gsm->tx1_list, list) {
> + dlci = gsm->dlci[msg->addr];
> + /* Send only messages for DLCIs with valid state */
> + if (dlci->state != DLCI_OPEN) {
> + gsm->tx_bytes -= msg->len;
> + list_del(&msg->list);
> + kfree(msg);
> + continue;
> + }
> + ret = gsm_send_packet(gsm, msg);
> + switch (ret) {
> + case -ENOSPC:
> + return -ENOSPC;
> + case -ENODEV:
> + /* ldisc not open */
> + gsm->tx_bytes -= msg->len;
> + list_del(&msg->list);
> + kfree(msg);
> + continue;
> + default:
> + if (ret >= 0) {
> + list_del(&msg->list);
> + kfree(msg);
> + }
> + break;
> }
> }
> +
> + return 1;
> }
That feels like a LOT of code shuffling. It's unreviewable. Please split
into several patches.
thanks,
--
js
suse labs
Greeting,
FYI, we noticed the following commit (built with gcc-11):
commit: 1e5b709515750b681ad1bb2c0c0929c701186101 ("[PATCH 2/2] tty: n_gsm: fix deadlock and link starvation in outgoing data path")
url: https://github.com/intel-lab-lkp/linux/commits/D-Starke/tty-n_gsm-fix-user-open-not-possible-at-responder-until-initiator-open/20220506-225117
base: https://git.kernel.org/cgit/linux/kernel/git/gregkh/tty.git tty-testing
patch link: https://lore.kernel.org/linux-serial/[email protected]
in testcase: ltp
version: ltp-x86_64-14c1f76-1_20220507
with following parameters:
test: cve
ucode: 0x28
test-description: The LTP testsuite contains a collection of tools for testing the Linux kernel and related features.
test-url: http://linux-test-project.github.io/
on test machine: 8 threads 1 sockets Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz with 16G memory
caused below changes (please refer to attached dmesg/kmsg for entire log/backtrace):
If you fix the issue, kindly add following tag
Reported-by: kernel test robot <[email protected]>
[ 1442.946913][ T4714] BUG: sleeping function called from invalid context at kernel/locking/mutex.c:280
[ 1442.949553][ T395] pty03.c:91: TCONF: You don't appear to have the CAIF TTY line discipline: EINVAL (22)
[ 1442.956001][ T4714] in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 4714, name: pty03
[ 1442.956005][ T4714] preempt_count: 1, expected: 0
[ 1442.956008][ T4714] CPU: 7 PID: 4714 Comm: pty03 Not tainted 5.18.0-rc5-00130-g1e5b70951575 #1
[ 1442.965530][ T395]
[ 1442.973912][ T4714] Hardware name: Dell Inc. OptiPlex 9020/0DNKMN, BIOS A05 12/05/2013
[ 1442.973914][ T4714] Call Trace:
[ 1442.973916][ T4714] <TASK>
[ 1442.973918][ T4714] dump_stack_lvl (lib/dump_stack.c:107 (discriminator 1))
[ 1442.973926][ T4714] __might_resched.cold (kernel/sched/core.c:9740)
[ 1442.979660][ T395] pty03.c:106: TINFO: Creating PTY with GSM line discipline
[ 1442.987172][ T4714] ? _raw_write_lock_irq (kernel/locking/spinlock.c:153)
[ 1442.989356][ T395]
[ 1442.997218][ T4714] mutex_lock (kernel/locking/mutex.c:280)
[ 1442.997223][ T4714] ? __mutex_lock_slowpath (kernel/locking/mutex.c:279)
[ 1442.997228][ T4714] ? klist_put (arch/x86/include/asm/atomic.h:190 include/linux/atomic/atomic-instrumented.h:177 include/linux/refcount.h:272 include/linux/refcount.h:315 include/linux/refcount.h:333 include/linux/kref.h:64 lib/klist.c:206 lib/klist.c:217)
[ 1443.040174][ T4714] device_del (drivers/base/core.c:3527 drivers/base/core.c:3555)
[ 1443.044250][ T4714] ? class_for_each_device (drivers/base/class.c:402)
[ 1443.049533][ T4714] ? __device_link_del (drivers/base/core.c:3548)
[ 1443.054472][ T4714] ? gsm_cleanup_mux (drivers/tty/n_gsm.c:2405) n_gsm
[ 1443.059931][ T4714] device_unregister (drivers/base/core.c:3511 drivers/base/core.c:3625)
[ 1443.064523][ T4714] device_destroy (drivers/base/core.c:4164)
[ 1443.068857][ T4714] ? root_device_unregister (drivers/base/core.c:4164)
[ 1443.074053][ T4714] ? _raw_read_unlock_irqrestore (kernel/locking/spinlock.c:161)
[ 1443.079679][ T4714] ? down_write (arch/x86/include/asm/atomic64_64.h:34 include/linux/atomic/atomic-long.h:41 include/linux/atomic/atomic-instrumented.h:1280 kernel/locking/rwsem.c:138 kernel/locking/rwsem.c:255 kernel/locking/rwsem.c:1258 kernel/locking/rwsem.c:1268 kernel/locking/rwsem.c:1515)
[ 1443.083926][ T4714] tty_unregister_device (drivers/tty/tty_io.c:3301)
[ 1443.088953][ T4714] gsmld_close (drivers/tty/n_gsm.c:2788 drivers/tty/n_gsm.c:2865) n_gsm
[ 1443.093804][ T4714] tty_ldisc_hangup (drivers/tty/tty_ldisc.c:609 drivers/tty/tty_ldisc.c:724)
[ 1443.098485][ T4714] ? fasync_remove_entry (fs/fcntl.c:869)
[ 1443.103508][ T4714] __tty_hangup+0x410/0x8c0
[ 1443.108447][ T4714] tty_ioctl (drivers/tty/tty_io.c:2719)
[ 1443.112606][ T4714] ? do_sys_openat2 (fs/open.c:1223)
[ 1443.117285][ T4714] ? tty_release (drivers/tty/tty_io.c:2655)
[ 1443.121705][ T4714] ? do_sys_openat2 (fs/open.c:1223)
[ 1443.126384][ T4714] ? build_open_flags (fs/open.c:1199)
[ 1443.131237][ T4714] ? __ia32_sys_stat (fs/stat.c:396)
[ 1443.135832][ T4714] ? userns_owner (kernel/user_namespace.c:371)
[ 1443.140169][ T4714] ? __fget_files (arch/x86/include/asm/atomic64_64.h:22 include/linux/atomic/atomic-arch-fallback.h:2293 include/linux/atomic/atomic-arch-fallback.h:2318 include/linux/atomic/atomic-long.h:491 include/linux/atomic/atomic-instrumented.h:1846 fs/file.c:903 fs/file.c:934)
[ 1443.144675][ T4714] __x64_sys_ioctl (fs/ioctl.c:52 fs/ioctl.c:870 fs/ioctl.c:856 fs/ioctl.c:856)
[ 1443.149268][ T4714] do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80)
[ 1443.153517][ T4714] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:115)
[ 1443.159232][ T4714] RIP: 0033:0x7fb8f2f1de57
[ 1443.163478][ T4714] Code: 00 00 90 48 8b 05 39 a0 0c 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 09 a0 0c 00 f7 d8 64 89 01 48
All code
========
0: 00 00 add %al,(%rax)
2: 90 nop
3: 48 8b 05 39 a0 0c 00 mov 0xca039(%rip),%rax # 0xca043
a: 64 c7 00 26 00 00 00 movl $0x26,%fs:(%rax)
11: 48 c7 c0 ff ff ff ff mov $0xffffffffffffffff,%rax
18: c3 retq
19: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
20: 00 00 00
23: b8 10 00 00 00 mov $0x10,%eax
28: 0f 05 syscall
2a:* 48 3d 01 f0 ff ff cmp $0xfffffffffffff001,%rax <-- trapping instruction
30: 73 01 jae 0x33
32: c3 retq
33: 48 8b 0d 09 a0 0c 00 mov 0xca009(%rip),%rcx # 0xca043
3a: f7 d8 neg %eax
3c: 64 89 01 mov %eax,%fs:(%rcx)
3f: 48 rex.W
Code starting with the faulting instruction
===========================================
0: 48 3d 01 f0 ff ff cmp $0xfffffffffffff001,%rax
6: 73 01 jae 0x9
8: c3 retq
9: 48 8b 0d 09 a0 0c 00 mov 0xca009(%rip),%rcx # 0xca019
10: f7 d8 neg %eax
12: 64 89 01 mov %eax,%fs:(%rcx)
15: 48 rex.W
[ 1443.182840][ T4714] RSP: 002b:00007ffcbe754398 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
[ 1443.191059][ T4714] RAX: ffffffffffffffda RBX: 0000559c76f23730 RCX: 00007fb8f2f1de57
[ 1443.198845][ T4714] RDX: 00000000002a8688 RSI: 0000000000005437 RDI: 000000000000000f
[ 1443.206634][ T4714] RBP: 0000559c76f23b00 R08: 00000000002a8688 R09: 0000000000000004
[ 1443.214420][ T4714] R10: 00007ffcbe7ef170 R11: 0000000000000246 R12: 0000000000000009
[ 1443.222207][ T4714] R13: 0000559c76f236a0 R14: 000000000000000f R15: 0000000000000000
[ 1443.229995][ T4714] </TASK>
[ 1443.232923][ T4714] BUG: scheduling while atomic: pty03/4714/0x00000002
[ 1443.239709][ T4714] Modules linked in: n_gsm pps_ldisc slcan xfs ext2 loop sctp ip6_udp_tunnel udp_tunnel authenc pcrypt crypto_user sha3_generic n_hdlc ipmi_devintf btrfs ipmi_msghandler blake2b_generic xor raid6_pq zstd_compress libcrc32c i915 sd_mod t10_pi crc64_rocksoft_generic crc64_rocksoft crc64 sg intel_rapl_msr intel_rapl_common x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel kvm irqbypass intel_gtt crct10dif_pclmul drm_buddy crc32_pclmul crc32c_intel drm_dp_helper ghash_clmulni_intel ttm rapl drm_kms_helper intel_cstate ahci syscopyarea mei_wdt sysfillrect libahci sysimgblt fb_sys_fops mei_me drm libata intel_uncore mei video ip_tables
[ 1443.297451][ T4714] CPU: 7 PID: 4714 Comm: pty03 Tainted: G W 5.18.0-rc5-00130-g1e5b70951575 #1
[ 1443.307411][ T4714] Hardware name: Dell Inc. OptiPlex 9020/0DNKMN, BIOS A05 12/05/2013
[ 1443.315284][ T4714] Call Trace:
[ 1443.318405][ T4714] <TASK>
[ 1443.321183][ T4714] dump_stack_lvl (lib/dump_stack.c:107 (discriminator 1))
[ 1443.325515][ T4714] __schedule_bug.cold (kernel/sched/core.c:5618)
[ 1443.330281][ T4714] schedule_debug (arch/x86/include/asm/preempt.h:35 kernel/sched/core.c:5645)
[ 1443.334787][ T4714] __schedule (arch/x86/include/asm/jump_label.h:27 include/linux/jump_label.h:207 kernel/sched/features.h:40 kernel/sched/core.c:6281)
[ 1443.338948][ T4714] ? _raw_read_unlock_irqrestore (kernel/locking/spinlock.c:161)
[ 1443.344576][ T4714] ? io_schedule_timeout (kernel/sched/core.c:6267)
[ 1443.349687][ T4714] ? update_load_avg (kernel/sched/fair.c:3647 kernel/sched/fair.c:3902)
[ 1443.354540][ T4714] schedule (include/linux/instrumented.h:71 (discriminator 1) include/asm-generic/bitops/instrumented-non-atomic.h:134 (discriminator 1) include/linux/thread_info.h:118 (discriminator 1) include/linux/sched.h:2154 (discriminator 1) kernel/sched/core.c:6462 (discriminator 1))
[ 1443.358442][ T4714] schedule_timeout (kernel/time/timer.c:1861)
[ 1443.363121][ T4714] ? usleep_range_state (kernel/time/timer.c:1846)
[ 1443.368147][ T4714] ? asm_sysvec_apic_timer_interrupt (arch/x86/include/asm/idtentry.h:645)
[ 1443.374121][ T4714] __wait_for_common (kernel/sched/completion.c:86 kernel/sched/completion.c:106)
[ 1443.378885][ T4714] ? usleep_range_state (kernel/time/timer.c:1846)
[ 1443.383910][ T4714] ? out_of_line_wait_on_bit_timeout (kernel/sched/completion.c:100)
[ 1443.390057][ T4714] ? _raw_spin_lock (arch/x86/include/asm/atomic.h:202 include/linux/atomic/atomic-instrumented.h:543 include/asm-generic/qspinlock.h:82 include/linux/spinlock.h:185 include/linux/spinlock_api_smp.h:134 kernel/locking/spinlock.c:154)
[ 1443.394647][ T4714] ? _raw_write_lock_irq (kernel/locking/spinlock.c:153)
[ 1443.399765][ T4714] ? __radix_tree_delete (arch/x86/include/asm/bitops.h:214 include/asm-generic/bitops/instrumented-non-atomic.h:135 lib/radix-tree.c:113 lib/radix-tree.c:941 lib/radix-tree.c:1372)
[ 1443.404885][ T4714] devtmpfs_submit_req (drivers/base/devtmpfs.c:116)
[ 1443.409737][ T4714] devtmpfs_delete_node (drivers/base/devtmpfs.c:149)
[ 1443.414674][ T4714] ? devtmpfs_create_node (drivers/base/devtmpfs.c:149)
[ 1443.419870][ T4714] ? kasan_set_free_info (mm/kasan/generic.c:372)
[ 1443.424809][ T4714] ? kernfs_put (fs/kernfs/dir.c:1703)
[ 1443.429748][ T4714] ? kernfs_put (fs/kernfs/dir.c:1703)
[ 1443.434695][ T4714] device_del (drivers/base/core.c:3574)
[ 1443.438856][ T4714] ? class_for_each_device (drivers/base/class.c:402)
[ 1443.444139][ T4714] ? __device_link_del (drivers/base/core.c:3548)
[ 1443.449077][ T4714] ? gsm_cleanup_mux (drivers/tty/n_gsm.c:2405) n_gsm
[ 1443.454535][ T4714] device_unregister (drivers/base/core.c:3511 drivers/base/core.c:3625)
[ 1443.459129][ T4714] device_destroy (drivers/base/core.c:4164)
[ 1443.463464][ T4714] ? root_device_unregister (drivers/base/core.c:4164)
[ 1443.468661][ T4714] ? _raw_read_unlock_irqrestore (kernel/locking/spinlock.c:161)
[ 1443.474288][ T4714] ? down_write (arch/x86/include/asm/atomic64_64.h:34 include/linux/atomic/atomic-long.h:41 include/linux/atomic/atomic-instrumented.h:1280 kernel/locking/rwsem.c:138 kernel/locking/rwsem.c:255 kernel/locking/rwsem.c:1258 kernel/locking/rwsem.c:1268 kernel/locking/rwsem.c:1515)
[ 1443.478536][ T4714] tty_unregister_device (drivers/tty/tty_io.c:3301)
[ 1443.483558][ T4714] gsmld_close (drivers/tty/n_gsm.c:2788 drivers/tty/n_gsm.c:2865) n_gsm
[ 1443.488411][ T4714] tty_ldisc_hangup (drivers/tty/tty_ldisc.c:609 drivers/tty/tty_ldisc.c:724)
[ 1443.493087][ T4714] ? fasync_remove_entry (fs/fcntl.c:869)
[ 1443.498110][ T4714] __tty_hangup+0x410/0x8c0
[ 1443.503048][ T4714] tty_ioctl (drivers/tty/tty_io.c:2719)
[ 1443.507210][ T4714] ? do_sys_openat2 (fs/open.c:1223)
[ 1443.511887][ T4714] ? tty_release (drivers/tty/tty_io.c:2655)
[ 1443.516307][ T4714] ? do_sys_openat2 (fs/open.c:1223)
[ 1443.520986][ T4714] ? build_open_flags (fs/open.c:1199)
[ 1443.525838][ T4714] ? __ia32_sys_stat (fs/stat.c:396)
[ 1443.530429][ T4714] ? userns_owner (kernel/user_namespace.c:371)
[ 1443.534764][ T4714] ? __fget_files (arch/x86/include/asm/atomic64_64.h:22 include/linux/atomic/atomic-arch-fallback.h:2293 include/linux/atomic/atomic-arch-fallback.h:2318 include/linux/atomic/atomic-long.h:491 include/linux/atomic/atomic-instrumented.h:1846 fs/file.c:903 fs/file.c:934)
[ 1443.539279][ T4714] __x64_sys_ioctl (fs/ioctl.c:52 fs/ioctl.c:870 fs/ioctl.c:856 fs/ioctl.c:856)
[ 1443.543872][ T4714] do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80)
[ 1443.548118][ T4714] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:115)
[ 1443.553832][ T4714] RIP: 0033:0x7fb8f2f1de57
[ 1443.558078][ T4714] Code: 00 00 90 48 8b 05 39 a0 0c 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 09 a0 0c 00 f7 d8 64 89 01 48
All code
========
0: 00 00 add %al,(%rax)
2: 90 nop
3: 48 8b 05 39 a0 0c 00 mov 0xca039(%rip),%rax # 0xca043
a: 64 c7 00 26 00 00 00 movl $0x26,%fs:(%rax)
11: 48 c7 c0 ff ff ff ff mov $0xffffffffffffffff,%rax
18: c3 retq
19: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
20: 00 00 00
23: b8 10 00 00 00 mov $0x10,%eax
28: 0f 05 syscall
2a:* 48 3d 01 f0 ff ff cmp $0xfffffffffffff001,%rax <-- trapping instruction
30: 73 01 jae 0x33
32: c3 retq
33: 48 8b 0d 09 a0 0c 00 mov 0xca009(%rip),%rcx # 0xca043
3a: f7 d8 neg %eax
3c: 64 89 01 mov %eax,%fs:(%rcx)
3f: 48 rex.W
Code starting with the faulting instruction
===========================================
0: 48 3d 01 f0 ff ff cmp $0xfffffffffffff001,%rax
6: 73 01 jae 0x9
8: c3 retq
9: 48 8b 0d 09 a0 0c 00 mov 0xca009(%rip),%rcx # 0xca019
10: f7 d8 neg %eax
12: 64 89 01 mov %eax,%fs:(%rcx)
15: 48 rex.W
[ 1443.577438][ T4714] RSP: 002b:00007ffcbe754398 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
[ 1443.585656][ T4714] RAX: ffffffffffffffda RBX: 0000559c76f23730 RCX: 00007fb8f2f1de57
[ 1443.593441][ T4714] RDX: 00000000002a8688 RSI: 0000000000005437 RDI: 000000000000000f
[ 1443.601227][ T4714] RBP: 0000559c76f23b00 R08: 00000000002a8688 R09: 0000000000000004
[ 1443.609011][ T4714] R10: 00007ffcbe7ef170 R11: 0000000000000246 R12: 0000000000000009
[ 1443.616797][ T4714] R13: 0000559c76f236a0 R14: 000000000000000f R15: 0000000000000000
[ 1443.624584][ T4714] </TASK>
[ 1443.639490][ C0] ------------[ cut here ]------------
[ 1443.639493][ C0] WARNING: CPU: 0 PID: 4714 at lib/usercopy.c:31 _copy_to_user (lib/usercopy.c:31)
[ 1443.639500][ C0] Modules linked in:
[ 1443.639502][ T4714] n_gsm pps_ldisc slcan xfs ext2 loop sctp ip6_udp_tunnel udp_tunnel authenc pcrypt crypto_user sha3_generic n_hdlc ipmi_devintf btrfs ipmi_msghandler blake2b_generic xor raid6_pq zstd_compress libcrc32c i915 sd_mod t10_pi crc64_rocksoft_generic crc64_rocksoft crc64 sg intel_rapl_msr intel_rapl_common x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel kvm irqbypass intel_gtt crct10dif_pclmul drm_buddy crc32_pclmul crc32c_intel drm_dp_helper ghash_clmulni_intel ttm rapl drm_kms_helper intel_cstate ahci syscopyarea mei_wdt sysfillrect libahci sysimgblt fb_sys_fops mei_me drm libata intel_uncore mei video ip_tables
[ 1443.656845][ C0]
[ 1443.656847][ C0] CPU: 0 PID: 4714 Comm: pty03 Tainted: G W 5.18.0-rc5-00130-g1e5b70951575 #1
[ 1443.656850][ C0] Hardware name: Dell Inc. OptiPlex 9020/0DNKMN, BIOS A05 12/05/2013
[ 1443.656851][ C0] RIP: 0010:_copy_to_user (lib/usercopy.c:31)
[ 1443.656855][ C0] Code: 39 c5 77 1c 44 89 e6 48 89 df e8 b8 cd 7f ff 48 89 ef 48 89 de 44 89 e2 e8 da e6 12 00 41 89 c4 5b 4c 89 e0 5d 41 5c 41 5d c3 <0f> 0b eb bf e8 54 c8 7f ff eb ad cc cc cc cc cc cc cc cc cc cc cc
All code
========
0: 39 c5 cmp %eax,%ebp
2: 77 1c ja 0x20
4: 44 89 e6 mov %r12d,%esi
7: 48 89 df mov %rbx,%rdi
a: e8 b8 cd 7f ff callq 0xffffffffff7fcdc7
f: 48 89 ef mov %rbp,%rdi
12: 48 89 de mov %rbx,%rsi
15: 44 89 e2 mov %r12d,%edx
18: e8 da e6 12 00 callq 0x12e6f7
1d: 41 89 c4 mov %eax,%r12d
20: 5b pop %rbx
21: 4c 89 e0 mov %r12,%rax
24: 5d pop %rbp
25: 41 5c pop %r12
27: 41 5d pop %r13
29: c3 retq
2a:* 0f 0b ud2 <-- trapping instruction
2c: eb bf jmp 0xffffffffffffffed
2e: e8 54 c8 7f ff callq 0xffffffffff7fc887
33: eb ad jmp 0xffffffffffffffe2
35: cc int3
36: cc int3
37: cc int3
38: cc int3
39: cc int3
3a: cc int3
3b: cc int3
3c: cc int3
3d: cc int3
3e: cc int3
3f: cc int3
Code starting with the faulting instruction
===========================================
0: 0f 0b ud2
2: eb bf jmp 0xffffffffffffffc3
4: e8 54 c8 7f ff callq 0xffffffffff7fc85d
9: eb ad jmp 0xffffffffffffffb8
b: cc int3
c: cc int3
d: cc int3
e: cc int3
f: cc int3
10: cc int3
11: cc int3
12: cc int3
13: cc int3
14: cc int3
15: cc int3
[ 1443.656857][ C0] RSP: 0018:ffffc9000043fe00 EFLAGS: 00010246
[ 1443.656859][ C0] RAX: 0000000000000000 RBX: ffffc9000043fe48 RCX: 1ffffffff0baeaf8
[ 1443.656861][ C0] RDX: 1ffff11080137988 RSI: 000000000000001c RDI: ffff8884009bcc40
[ 1443.656863][ C0] RBP: 0000559c76f2dc78 R08: 0000000003a71299 R09: 0000000000000000
[ 1443.656864][ C0] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000010
[ 1443.656865][ C0] R13: ffff8884009ba940 R14: 0000000000000001 R15: 0000000000000000
[ 1443.656867][ C0] FS: 00007fb8f2e2b740(0000) GS:ffff888381400000(0000) knlGS:0000000000000000
[ 1443.656869][ C0] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 1443.656871][ C0] CR2: 00005592e47a7d00 CR3: 000000015316a005 CR4: 00000000001706f0
[ 1443.656872][ C0] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 1443.656873][ C0] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[ 1443.656875][ C0] Call Trace:
[ 1443.656876][ C0] <TASK>
[ 1443.656877][ C0] put_timespec64 (kernel/time/time.c:806)
[ 1443.656881][ C0] ? __ia32_sys_stime32 (kernel/time/time.c:806)
[ 1443.656883][ C0] ? _raw_spin_lock (arch/x86/include/asm/atomic.h:202 include/linux/atomic/atomic-instrumented.h:543 include/asm-generic/qspinlock.h:82 include/linux/spinlock.h:185 include/linux/spinlock_api_smp.h:134 kernel/locking/spinlock.c:154)
[ 1443.656886][ C0] __x64_sys_clock_gettime (kernel/time/posix-timers.c:1094 kernel/time/posix-timers.c:1082 kernel/time/posix-timers.c:1082)
[ 1443.656890][ C0] ? __x64_sys_clock_gettime32 (kernel/time/posix-timers.c:1082)
[ 1443.656892][ C0] ? fput_many (fs/file_table.c:387 fs/file_table.c:371)
[ 1443.656895][ C0] do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80)
[ 1443.656898][ C0] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:115)
[ 1443.656901][ C0] RIP: 0033:0x7fb8f2f21989
[ 1443.656903][ C0] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d d7 64 0c 00 f7 d8 64 89 01 48
All code
========
0: 00 c3 add %al,%bl
2: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
9: 00 00 00
c: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
11: 48 89 f8 mov %rdi,%rax
14: 48 89 f7 mov %rsi,%rdi
17: 48 89 d6 mov %rdx,%rsi
1a: 48 89 ca mov %rcx,%rdx
1d: 4d 89 c2 mov %r8,%r10
20: 4d 89 c8 mov %r9,%r8
23: 4c 8b 4c 24 08 mov 0x8(%rsp),%r9
28: 0f 05 syscall
2a:* 48 3d 01 f0 ff ff cmp $0xfffffffffffff001,%rax <-- trapping instruction
30: 73 01 jae 0x33
32: c3 retq
33: 48 8b 0d d7 64 0c 00 mov 0xc64d7(%rip),%rcx # 0xc6511
3a: f7 d8 neg %eax
3c: 64 89 01 mov %eax,%fs:(%rcx)
3f: 48 rex.W
Code starting with the faulting instruction
===========================================
0: 48 3d 01 f0 ff ff cmp $0xfffffffffffff001,%rax
6: 73 01 jae 0x9
8: c3 retq
9: 48 8b 0d d7 64 0c 00 mov 0xc64d7(%rip),%rcx # 0xc64e7
10: f7 d8 neg %eax
12: 64 89 01 mov %eax,%fs:(%rcx)
15: 48 rex.W
To reproduce:
git clone https://github.com/intel/lkp-tests.git
cd lkp-tests
sudo bin/lkp install job.yaml # job file is attached in this email
bin/lkp split-job --compatible job.yaml # generate the yaml file for lkp run
sudo bin/lkp run generated-yaml-file
# if come across any failure that blocks the test,
# please remove ~/.lkp and /lkp dir to run from a clean state.
--
0-DAY CI Kernel Test Service
https://01.org/lkp