blob: d649a1ed124fa3ce7bf819c8553036eadad5bee2 [file] [log] [blame]
From df04ddde6d9577c3cf230e725f36ae2480908040 Mon Sep 17 00:00:00 2001
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Date: Thu, 24 Jan 2019 17:43:52 +0100
Subject: [PATCH 09/15] tty: n_r3964: for messages, use a real kernel list
The message list associated with a client was a hand-rolled linked list
structure, so use a "normal" kernel list structure instead. This makes
the code smaller and simpler to understand.
In doing so, fix up a number of locking mistakes where the proper lock
was not being held for every time the client message list was being
accessed.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/tty/n_r3964.c | 136 +++++++++++++++++++++++---------------------------
1 file changed, 64 insertions(+), 72 deletions(-)
--- a/drivers/tty/n_r3964.c
+++ b/drivers/tty/n_r3964.c
@@ -101,7 +101,6 @@ enum {
/* All open file-handles are 'clients' and are stored in a linked list: */
-struct r3964_message;
struct rx_block_header;
struct tx_block_header;
@@ -112,8 +111,7 @@ struct r3964_client_info {
struct r3964_client_info *next;
- struct r3964_message *first_msg;
- struct r3964_message *last_msg;
+ struct list_head msgs;
struct rx_block_header *next_block_to_read;
int msg_count;
};
@@ -124,7 +122,7 @@ struct r3964_message {
int arg;
int error_code;
struct rx_block_header *block;
- struct r3964_message *next;
+ struct list_head node;
};
/* Header of received block in rx_buf: */
@@ -200,8 +198,7 @@ static int read_telegram(struct r3964_in
unsigned char __user * buf);
static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg,
int error_code, struct rx_block_header *pBlock);
-static struct r3964_message *remove_msg(struct r3964_info *pInfo,
- struct r3964_client_info *pClient);
+static struct r3964_message *remove_msg(struct r3964_client_info *client);
static void remove_client_block(struct r3964_client_info *pClient);
static int r3964_open(struct tty_struct *tty);
@@ -788,7 +785,7 @@ static int enable_signals(struct r3964_i
pid_nr(pid));
*ppClient = pClient->next;
while (pClient->msg_count) {
- pMsg = remove_msg(pInfo, pClient);
+ pMsg = remove_msg(pClient);
if (pMsg) {
kfree(pMsg);
TRACE_M("enable_signals - msg "
@@ -823,8 +820,7 @@ static int enable_signals(struct r3964_i
pClient->sig_flags = arg;
pClient->pid = get_pid(pid);
pClient->next = pInfo->firstClient;
- pClient->first_msg = NULL;
- pClient->last_msg = NULL;
+ INIT_LIST_HEAD(&pClient->msgs);
pClient->next_block_to_read = NULL;
pClient->msg_count = 0;
pInfo->firstClient = pClient;
@@ -864,85 +860,83 @@ static int read_telegram(struct r3964_in
return -EINVAL;
}
-static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg,
- int error_code, struct rx_block_header *pBlock)
+static void __add_msg(struct r3964_client_info *pClient, int msg_id, int arg,
+ int error_code, struct rx_block_header *pBlock)
+ __must_hold(&pClient->lock)
{
struct r3964_message *pMsg;
- unsigned long flags;
-
- if (pClient->msg_count < R3964_MAX_MSG_COUNT - 1) {
-queue_the_message:
- pMsg = kmalloc(sizeof(*pMsg), GFP_ATOMIC);
- TRACE_M("add_msg - kmalloc %p", pMsg);
- if (pMsg == NULL) {
- return;
- }
+ pMsg = kmalloc(sizeof(*pMsg), GFP_ATOMIC);
+ if (pMsg == NULL)
+ return;
- spin_lock_irqsave(&pClient->lock, flags);
+ pMsg->msg_id = msg_id;
+ pMsg->arg = arg;
+ pMsg->error_code = error_code;
+ pMsg->block = pBlock;
+ INIT_LIST_HEAD(&pMsg->node);
- pMsg->msg_id = msg_id;
- pMsg->arg = arg;
- pMsg->error_code = error_code;
- pMsg->block = pBlock;
- pMsg->next = NULL;
+ list_add_tail(&pMsg->node, &pClient->msgs);
+ pClient->msg_count++;
- if (pClient->last_msg == NULL) {
- pClient->first_msg = pClient->last_msg = pMsg;
- } else {
- pClient->last_msg->next = pMsg;
- pClient->last_msg = pMsg;
- }
+ if (pBlock != NULL)
+ kref_get(&pBlock->kref);
+}
- pClient->msg_count++;
+static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg,
+ int error_code, struct rx_block_header *pBlock)
+{
+ struct r3964_message *pMsg;
+ unsigned long flags;
- if (pBlock != NULL)
- kref_get(&pBlock->kref);
+ spin_lock_irqsave(&pClient->lock, flags);
- spin_unlock_irqrestore(&pClient->lock, flags);
+ if (pClient->msg_count < R3964_MAX_MSG_COUNT - 1) {
+ __add_msg(pClient, msg_id, arg, error_code, pBlock);
} else {
- if ((pClient->last_msg->msg_id == R3964_MSG_ACK)
- && (pClient->last_msg->error_code == R3964_OVERFLOW)) {
- pClient->last_msg->arg++;
- TRACE_PE("add_msg - inc prev OVERFLOW-msg");
+ if (!list_empty(&pClient->msgs)) {
+ pMsg = list_last_entry(&pClient->msgs,
+ struct r3964_message, node);
+ if ((pMsg->msg_id == R3964_MSG_ACK) &&
+ (pMsg->error_code == R3964_OVERFLOW)) {
+ pMsg->arg++;
+ TRACE_PE("add_msg - inc prev OVERFLOW-msg");
+ }
} else {
- msg_id = R3964_MSG_ACK;
- arg = 0;
- error_code = R3964_OVERFLOW;
- pBlock = NULL;
+ __add_msg(pClient, R3964_MSG_ACK, 0, R3964_OVERFLOW,
+ pBlock);
TRACE_PE("add_msg - queue OVERFLOW-msg");
- goto queue_the_message;
}
}
+ spin_unlock_irqrestore(&pClient->lock, flags);
+
/* Send SIGIO signal to client process: */
if (pClient->sig_flags & R3964_USE_SIGIO) {
kill_pid(pClient->pid, SIGIO, 1);
}
}
-static struct r3964_message *remove_msg(struct r3964_info *pInfo,
- struct r3964_client_info *pClient)
+static struct r3964_message *remove_msg(struct r3964_client_info *client)
{
- struct r3964_message *pMsg = NULL;
+ struct r3964_message *msg = NULL;
unsigned long flags;
- if (pClient->first_msg) {
- spin_lock_irqsave(&pClient->lock, flags);
-
- pMsg = pClient->first_msg;
- pClient->first_msg = pMsg->next;
- if (pClient->first_msg == NULL) {
- pClient->last_msg = NULL;
- }
-
- pClient->msg_count--;
- if (pMsg->block) {
- remove_client_block(pClient);
- pClient->next_block_to_read = pMsg->block;
- }
- spin_unlock_irqrestore(&pClient->lock, flags);
+ spin_lock_irqsave(&client->lock, flags);
+ if (list_empty(&client->msgs)) {
+ spin_unlock_irqrestore(&client->lock, flags);
+ return NULL;
+ }
+
+ msg = list_first_entry(&client->msgs, struct r3964_message, node);
+ list_del(&msg->node);
+
+ client->msg_count--;
+ if (msg->block) {
+ remove_client_block(client);
+ client->next_block_to_read = msg->block;
}
- return pMsg;
+ spin_unlock_irqrestore(&client->lock, flags);
+ return msg;
}
static void remove_client_block(struct r3964_client_info *client)
@@ -1041,7 +1035,7 @@ static void r3964_close(struct tty_struc
while (pClient) {
pNext = pClient->next;
while (pClient->msg_count) {
- pMsg = remove_msg(pInfo, pClient);
+ pMsg = remove_msg(pClient);
if (pMsg) {
kfree(pMsg);
TRACE_M("r3964_close - msg kfree %p", pMsg);
@@ -1091,7 +1085,7 @@ static ssize_t r3964_read(struct tty_str
pClient = findClient(pInfo, task_pid(current));
if (pClient) {
- pMsg = remove_msg(pInfo, pClient);
+ pMsg = remove_msg(pClient);
if (pMsg == NULL) {
/* no messages available. */
if (tty_io_nonblock(tty, file)) {
@@ -1100,7 +1094,7 @@ static ssize_t r3964_read(struct tty_str
}
/* block until there is a message: */
wait_event_interruptible(tty->read_wait,
- (pMsg = remove_msg(pInfo, pClient)));
+ (pMsg = remove_msg(pClient)));
}
/* If we still haven't got a message, we must have been signalled */
@@ -1267,7 +1261,6 @@ static __poll_t r3964_poll(struct tty_st
{
struct r3964_info *pInfo = tty->disc_data;
struct r3964_client_info *pClient;
- struct r3964_message *pMsg = NULL;
unsigned long flags;
__poll_t result = EPOLLOUT;
@@ -1276,11 +1269,10 @@ static __poll_t r3964_poll(struct tty_st
pClient = findClient(pInfo, task_pid(current));
if (pClient) {
poll_wait(file, &tty->read_wait, wait);
- spin_lock_irqsave(&pInfo->lock, flags);
- pMsg = pClient->first_msg;
- spin_unlock_irqrestore(&pInfo->lock, flags);
- if (pMsg)
+ spin_lock_irqsave(&pClient->lock, flags);
+ if (!list_empty(&pClient->msgs))
result |= EPOLLIN | EPOLLRDNORM;
+ spin_unlock_irqrestore(&pClient->lock, flags);
} else {
result = EPOLLNVAL | EPOLLERR;
}