aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-05-15 10:20:16 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-05-15 10:20:16 +0200
commit2c3266e8829eb5a611a08cddca97f79c05ed28db (patch)
treeb7aaa09c1ee1f738acd83b9a46ae2d135f89b22c
parentcf6fd4d72fe78061c68af7a241387ec2d26dc161 (diff)
downloadpatches-2c3266e8829eb5a611a08cddca97f79c05ed28db.tar.gz
tty patches added
-rw-r--r--0001-tty-n_r3964-locking-fixups.patch212
-rw-r--r--0002-tty-n_r3964-fix-poll-return-value.patch30
-rw-r--r--0003-tty-n_r3964-remove-n_r3964.h.patch337
-rw-r--r--0004-tty-n_r3964-drop-ancient-header-changelog-text.patch74
-rw-r--r--0005-tty-n_r3964-split-rx-and-tx-header-structures.patch265
-rw-r--r--0006-tty-n_r3964-for-tx_blocks-use-a-real-kernel-list.patch187
-rw-r--r--0007-tty-n_r3964-for-rx_blocks-use-a-real-kernel-list.patch141
-rw-r--r--0008-tty-n_r3964-don-t-hand-roll-a-reference-count.patch160
-rw-r--r--0009-tty-n_r3964-for-messages-use-a-real-kernel-list.patch268
-rw-r--r--0010-tty-n_r3964-for-clients-use-a-real-kernel-list.patch148
-rw-r--r--0011-tty-n_r3964-fix-race-with-add_msg-and-read_telegram.patch113
-rw-r--r--0012-tty-n_r3964-remove-read_lock-from-some-ioctls.patch92
-rw-r--r--0013-tty-n_r3964-properly-protect-sig_flags-of-client-str.patch82
-rw-r--r--0014-tty-n_r3964-properly-reference-count-pids.patch103
-rw-r--r--0015-tty-n_r3964-add-reference-counting-to-the-client-str.patch250
-rw-r--r--p0412
-rw-r--r--series15
17 files changed, 2483 insertions, 6 deletions
diff --git a/0001-tty-n_r3964-locking-fixups.patch b/0001-tty-n_r3964-locking-fixups.patch
new file mode 100644
index 00000000000000..dbcbfeff34d067
--- /dev/null
+++ b/0001-tty-n_r3964-locking-fixups.patch
@@ -0,0 +1,212 @@
+From 51d25226b066ddeff053d36a74af38e53472520b Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Wed, 23 Jan 2019 11:00:42 +0100
+Subject: [PATCH 01/15] tty: n_r3964: locking fixups
+
+The n_r3964 line discipline has an "interesting" concept of locking. The
+list of client's are not always properly accessed under a lock, which
+can cause problems with some multi-threaded systems.
+
+To resolve this, do two different things:
+ - serialize ioctl and read accesses.
+ Right now ioctls can mess with the structures that a read call wants
+ to also touch, so serialze them to make it simpler. Note, this
+ _might_ break some userspace applications, as one thread could be
+ waiting in a read while another one wanted to make an ioctl call.
+ In reality, the ioctls mess with things so much that any outstanding
+ read might be really confused, so this is not a good thing for
+ userspace to be doing anyway.
+ - properly protect the client list
+ The list of clients could be accessed by different threads at the
+ same time without any locking. Well, there was some attempt at
+ locking, but the main access, findClient(), was not locked at all.
+ Also fix this up in a few other places.
+
+This line discipline needs a major overhaul. It was written at a time
+there was not any SMP machines, and it shows. Rewriting some of the
+object handling logic will allow the read/ioctl serialization to be
+removed again.
+
+Reported-by: Jann Horn <jannh@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 54 +++++++++++++++++++++++++++++++----------
+ include/linux/n_r3964.h | 2 +-
+ 2 files changed, 42 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index f75696f0ee2d..664b201b9abc 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -484,6 +484,7 @@ static void on_receive_block(struct r3964_info *pInfo)
+ unsigned int length;
+ struct r3964_client_info *pClient;
+ struct r3964_block_header *pBlock;
++ unsigned long flags;
+
+ length = pInfo->rx_position;
+
+@@ -541,12 +542,14 @@ static void on_receive_block(struct r3964_info *pInfo)
+ add_rx_queue(pInfo, pBlock);
+
+ /* notify attached client processes: */
++ spin_lock_irqsave(&pInfo->lock, flags);
+ for (pClient = pInfo->firstClient; pClient; pClient = pClient->next) {
+ if (pClient->sig_flags & R3964_SIG_DATA) {
+ add_msg(pClient, R3964_MSG_DATA, length, R3964_OK,
+ pBlock);
+ }
+ }
++ spin_unlock_irqrestore(&pInfo->lock, flags);
+ wake_up_interruptible(&pInfo->tty->read_wait);
+
+ pInfo->state = R3964_IDLE;
+@@ -743,13 +746,17 @@ static struct r3964_client_info *findClient(struct r3964_info *pInfo,
+ struct pid *pid)
+ {
+ struct r3964_client_info *pClient;
++ unsigned long flags;
+
++ spin_lock_irqsave(&pInfo->lock, flags);
+ for (pClient = pInfo->firstClient; pClient; pClient = pClient->next) {
+ if (pClient->pid == pid) {
+- return pClient;
++ goto exit;
+ }
+ }
+- return NULL;
++exit:
++ spin_unlock_irqrestore(&pInfo->lock, flags);
++ return pClient;
+ }
+
+ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+@@ -757,8 +764,11 @@ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+ struct r3964_client_info *pClient;
+ struct r3964_client_info **ppClient;
+ struct r3964_message *pMsg;
++ unsigned long flags;
+
+ if ((arg & R3964_SIG_ALL) == 0) {
++ spin_lock_irqsave(&pInfo->lock, flags);
++
+ /* Remove client from client list */
+ for (ppClient = &pInfo->firstClient; *ppClient;
+ ppClient = &(*ppClient)->next) {
+@@ -779,9 +789,11 @@ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+ put_pid(pClient->pid);
+ kfree(pClient);
+ TRACE_M("enable_signals - kfree %p", pClient);
++ spin_unlock_irqrestore(&pInfo->lock, flags);
+ return 0;
+ }
+ }
++ spin_unlock_irqrestore(&pInfo->lock, flags);
+ return -EINVAL;
+ } else {
+ pClient = findClient(pInfo, pid);
+@@ -796,6 +808,7 @@ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+ if (pClient == NULL)
+ return -ENOMEM;
+
++ spin_lock_irqsave(&pInfo->lock, flags);
+ TRACE_PS("add client %d to client list", pid_nr(pid));
+ spin_lock_init(&pClient->lock);
+ pClient->sig_flags = arg;
+@@ -806,6 +819,7 @@ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+ pClient->next_block_to_read = NULL;
+ pClient->msg_count = 0;
+ pInfo->firstClient = pClient;
++ spin_unlock_irqrestore(&pInfo->lock, flags);
+ }
+ }
+
+@@ -850,8 +864,7 @@ static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg,
+ if (pClient->msg_count < R3964_MAX_MSG_COUNT - 1) {
+ queue_the_message:
+
+- pMsg = kmalloc(sizeof(struct r3964_message),
+- error_code ? GFP_ATOMIC : GFP_KERNEL);
++ pMsg = kmalloc(sizeof(*pMsg), GFP_ATOMIC);
+ TRACE_M("add_msg - kmalloc %p", pMsg);
+ if (pMsg == NULL) {
+ return;
+@@ -1069,9 +1082,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
+
+ TRACE_L("read()");
+
+- /*
+- * Internal serialization of reads.
+- */
++ /* Internal serialization of reads and ioctls. */
+ if (file->f_flags & O_NONBLOCK) {
+ if (!mutex_trylock(&pInfo->read_lock))
+ return -EAGAIN;
+@@ -1193,28 +1204,45 @@ static int r3964_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+ {
+ struct r3964_info *pInfo = tty->disc_data;
++ int retval = 0;
++
+ if (pInfo == NULL)
+ return -EINVAL;
++ /* Internal serialization of reads and ioctls */
++ if (file->f_flags & O_NONBLOCK) {
++ if (!mutex_trylock(&pInfo->read_lock))
++ return -EAGAIN;
++ } else {
++ if (mutex_lock_interruptible(&pInfo->read_lock))
++ return -ERESTARTSYS;
++ }
++
+ switch (cmd) {
+ case R3964_ENABLE_SIGNALS:
+- return enable_signals(pInfo, task_pid(current), arg);
++ retval = enable_signals(pInfo, task_pid(current), arg);
++ break;
+ case R3964_SETPRIORITY:
+ if (arg < R3964_MASTER || arg > R3964_SLAVE)
+ return -EINVAL;
+ pInfo->priority = arg & 0xff;
+- return 0;
++ break;
+ case R3964_USE_BCC:
+ if (arg)
+ pInfo->flags |= R3964_BCC;
+ else
+ pInfo->flags &= ~R3964_BCC;
+- return 0;
++ break;
+ case R3964_READ_TELEGRAM:
+- return read_telegram(pInfo, task_pid(current),
+- (unsigned char __user *)arg);
++ retval = read_telegram(pInfo, task_pid(current),
++ (unsigned char __user *)arg);
++ break;
+ default:
+- return -ENOIOCTLCMD;
++ retval = -ENOIOCTLCMD;
++ break;
+ }
++
++ mutex_unlock(&pInfo->read_lock);
++ return retval;
+ }
+
+ #ifdef CONFIG_COMPAT
+diff --git a/include/linux/n_r3964.h b/include/linux/n_r3964.h
+index 90a803aa42e8..9cc0020930ad 100644
+--- a/include/linux/n_r3964.h
++++ b/include/linux/n_r3964.h
+@@ -162,7 +162,7 @@ struct r3964_info {
+ unsigned char bcc;
+ unsigned int blocks_in_rx_queue;
+
+- struct mutex read_lock; /* serialize r3964_read */
++ struct mutex read_lock; /* serialize read and ioctl */
+
+ struct r3964_client_info *firstClient;
+ unsigned int state;
+--
+2.21.0
+
diff --git a/0002-tty-n_r3964-fix-poll-return-value.patch b/0002-tty-n_r3964-fix-poll-return-value.patch
new file mode 100644
index 00000000000000..e2e91facfa6269
--- /dev/null
+++ b/0002-tty-n_r3964-fix-poll-return-value.patch
@@ -0,0 +1,30 @@
+From f93faa310cf3da0d54ee2232a76729b2063cc551 Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Fri, 25 Jan 2019 15:13:58 +0100
+Subject: [PATCH 02/15] tty: n_r3964: fix poll return value
+
+-EINVAL is not a valid __poll_t return value for when an error happens.
+Instead, set return EPOLLNVAL | EPOLLERR to tell userspace that what it
+wanted to have happen did not.
+
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index 664b201b9abc..b4a205f9f8ac 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -1286,7 +1286,7 @@ static __poll_t r3964_poll(struct tty_struct *tty, struct file *file,
+ if (pMsg)
+ result |= EPOLLIN | EPOLLRDNORM;
+ } else {
+- result = -EINVAL;
++ result = EPOLLNVAL | EPOLLERR;
+ }
+ return result;
+ }
+--
+2.21.0
+
diff --git a/0003-tty-n_r3964-remove-n_r3964.h.patch b/0003-tty-n_r3964-remove-n_r3964.h.patch
new file mode 100644
index 00000000000000..3831fbbcfef200
--- /dev/null
+++ b/0003-tty-n_r3964-remove-n_r3964.h.patch
@@ -0,0 +1,337 @@
+From 3383e7c9df4908b5f3ac97e5ac0492775d0a4b35 Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Wed, 23 Jan 2019 20:28:46 +0100
+Subject: [PATCH 03/15] tty: n_r3964: remove n_r3964.h
+
+No need to have a .h file in include/linux/ for a line discipline where
+the only things in it are needed by a single c file.
+
+So move the contents (and fix the formatting at the same time) into
+drivers/tty/n_r3964.c
+
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 114 +++++++++++++++++++++++++-
+ include/linux/n_r3964.h | 175 ----------------------------------------
+ 2 files changed, 113 insertions(+), 176 deletions(-)
+ delete mode 100644 include/linux/n_r3964.h
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index b4a205f9f8ac..62a0e869eab5 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -60,10 +60,11 @@
+ #include <linux/string.h> /* used in new tty drivers */
+ #include <linux/signal.h> /* used in new tty drivers */
+ #include <linux/ioctl.h>
+-#include <linux/n_r3964.h>
++#include <linux/param.h>
+ #include <linux/poll.h>
+ #include <linux/init.h>
+ #include <linux/uaccess.h>
++#include <uapi/linux/n_r3964.h>
+
+ /*#define DEBUG_QUEUE*/
+
+@@ -107,6 +108,117 @@
+ #else
+ #define TRACE_Q(fmt, arg...) do {} while (0)
+ #endif
++
++/* Common ascii handshake characters: */
++#define STX 0x02
++#define ETX 0x03
++#define DLE 0x10
++#define NAK 0x15
++
++/* Timeouts (from milliseconds to jiffies) */
++#define R3964_TO_QVZ ((550)*HZ/1000)
++#define R3964_TO_ZVZ ((220)*HZ/1000)
++#define R3964_TO_NO_BUF ((400)*HZ/1000)
++#define R3964_NO_TX_ROOM ((100)*HZ/1000)
++#define R3964_TO_RX_PANIC ((4000)*HZ/1000)
++#define R3964_MAX_RETRIES 5
++
++enum {
++ R3964_IDLE,
++ R3964_TX_REQUEST,
++ R3964_TRANSMITTING,
++ R3964_WAIT_ZVZ_BEFORE_TX_RETRY,
++ R3964_WAIT_FOR_TX_ACK,
++ R3964_WAIT_FOR_RX_BUF,
++ R3964_RECEIVING,
++ R3964_WAIT_FOR_BCC,
++ R3964_WAIT_FOR_RX_REPEAT
++};
++
++/* All open file-handles are 'clients' and are stored in a linked list: */
++
++struct r3964_message;
++
++struct r3964_client_info {
++ spinlock_t lock;
++ struct pid *pid;
++ unsigned int sig_flags;
++
++ struct r3964_client_info *next;
++
++ struct r3964_message *first_msg;
++ struct r3964_message *last_msg;
++ struct r3964_block_header *next_block_to_read;
++ int msg_count;
++};
++
++struct r3964_block_header;
++
++/* internal version of client_message: */
++struct r3964_message {
++ int msg_id;
++ int arg;
++ int error_code;
++ struct r3964_block_header *block;
++ struct r3964_message *next;
++};
++
++/* Header of received block in rx_buf/tx_buf: */
++struct r3964_block_header {
++ unsigned int length; /* length in chars without header */
++ unsigned char *data; /* usually data is located immediately
++ * behind this struct */
++ unsigned int locks; /* only used in rx_buffer */
++
++ struct r3964_block_header *next;
++ struct r3964_client_info *owner; /* =NULL in rx_buffer */
++};
++
++/*
++ * If rx_buf hasn't enough space to store R3964_MTU chars,
++ * we will reject all incoming STX-requests by sending NAK.
++ */
++#define RX_BUF_SIZE 4000
++#define TX_BUF_SIZE 4000
++#define R3964_MAX_BLOCKS_IN_RX_QUEUE 100
++
++#define R3964_PARITY 0x0001
++#define R3964_FRAME 0x0002
++#define R3964_OVERRUN 0x0004
++#define R3964_UNKNOWN 0x0008
++#define R3964_BREAK 0x0010
++#define R3964_CHECKSUM 0x0020
++#define R3964_ERROR 0x003f
++#define R3964_BCC 0x4000
++#define R3964_DEBUG 0x8000
++
++struct r3964_info {
++ spinlock_t lock;
++ struct tty_struct *tty;
++ unsigned char priority;
++ unsigned char *rx_buf; /* ring buffer */
++ unsigned char *tx_buf;
++
++ struct r3964_block_header *rx_first;
++ struct r3964_block_header *rx_last;
++ struct r3964_block_header *tx_first;
++ struct r3964_block_header *tx_last;
++ unsigned int tx_position;
++ unsigned int rx_position;
++ unsigned char last_rx;
++ unsigned char bcc;
++ unsigned int blocks_in_rx_queue;
++
++ struct mutex read_lock; /* serialize read and ioctl */
++
++ struct r3964_client_info *firstClient;
++ unsigned int state;
++ unsigned int flags;
++
++ struct timer_list tmr;
++ int nRetry;
++};
++
+ static void add_tx_queue(struct r3964_info *, struct r3964_block_header *);
+ static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code);
+ static void put_char(struct r3964_info *pInfo, unsigned char ch);
+diff --git a/include/linux/n_r3964.h b/include/linux/n_r3964.h
+deleted file mode 100644
+index 9cc0020930ad..000000000000
+--- a/include/linux/n_r3964.h
++++ /dev/null
+@@ -1,175 +0,0 @@
+-/* r3964 linediscipline for linux
+- *
+- * -----------------------------------------------------------
+- * Copyright by
+- * Philips Automation Projects
+- * Kassel (Germany)
+- * -----------------------------------------------------------
+- * This software may be used and distributed according to the terms of
+- * the GNU General Public License, incorporated herein by reference.
+- *
+- * Author:
+- * L. Haag
+- *
+- * $Log: r3964.h,v $
+- * Revision 1.4 2005/12/21 19:54:24 Kurt Huwig <kurt huwig de>
+- * Fixed HZ usage on 2.6 kernels
+- * Removed unnecessary include
+- *
+- * Revision 1.3 2001/03/18 13:02:24 dwmw2
+- * Fix timer usage, use spinlocks properly.
+- *
+- * Revision 1.2 2001/03/18 12:53:15 dwmw2
+- * Merge changes in 2.4.2
+- *
+- * Revision 1.1.1.1 1998/10/13 16:43:14 dwmw2
+- * This'll screw the version control
+- *
+- * Revision 1.6 1998/09/30 00:40:38 dwmw2
+- * Updated to use kernel's N_R3964 if available
+- *
+- * Revision 1.4 1998/04/02 20:29:44 lhaag
+- * select, blocking, ...
+- *
+- * Revision 1.3 1998/02/12 18:58:43 root
+- * fixed some memory leaks
+- * calculation of checksum characters
+- *
+- * Revision 1.2 1998/02/07 13:03:17 root
+- * ioctl read_telegram
+- *
+- * Revision 1.1 1998/02/06 19:19:43 root
+- * Initial revision
+- *
+- *
+- */
+-#ifndef __LINUX_N_R3964_H__
+-#define __LINUX_N_R3964_H__
+-
+-
+-#include <linux/param.h>
+-#include <uapi/linux/n_r3964.h>
+-
+-/*
+- * Common ascii handshake characters:
+- */
+-
+-#define STX 0x02
+-#define ETX 0x03
+-#define DLE 0x10
+-#define NAK 0x15
+-
+-/*
+- * Timeouts (from milliseconds to jiffies)
+- */
+-
+-#define R3964_TO_QVZ ((550)*HZ/1000)
+-#define R3964_TO_ZVZ ((220)*HZ/1000)
+-#define R3964_TO_NO_BUF ((400)*HZ/1000)
+-#define R3964_NO_TX_ROOM ((100)*HZ/1000)
+-#define R3964_TO_RX_PANIC ((4000)*HZ/1000)
+-#define R3964_MAX_RETRIES 5
+-
+-
+-enum { R3964_IDLE,
+- R3964_TX_REQUEST, R3964_TRANSMITTING,
+- R3964_WAIT_ZVZ_BEFORE_TX_RETRY, R3964_WAIT_FOR_TX_ACK,
+- R3964_WAIT_FOR_RX_BUF,
+- R3964_RECEIVING, R3964_WAIT_FOR_BCC, R3964_WAIT_FOR_RX_REPEAT
+- };
+-
+-/*
+- * All open file-handles are 'clients' and are stored in a linked list:
+- */
+-
+-struct r3964_message;
+-
+-struct r3964_client_info {
+- spinlock_t lock;
+- struct pid *pid;
+- unsigned int sig_flags;
+-
+- struct r3964_client_info *next;
+-
+- struct r3964_message *first_msg;
+- struct r3964_message *last_msg;
+- struct r3964_block_header *next_block_to_read;
+- int msg_count;
+-};
+-
+-
+-
+-struct r3964_block_header;
+-
+-/* internal version of client_message: */
+-struct r3964_message {
+- int msg_id;
+- int arg;
+- int error_code;
+- struct r3964_block_header *block;
+- struct r3964_message *next;
+-};
+-
+-/*
+- * Header of received block in rx_buf/tx_buf:
+- */
+-
+-struct r3964_block_header
+-{
+- unsigned int length; /* length in chars without header */
+- unsigned char *data; /* usually data is located
+- immediately behind this struct */
+- unsigned int locks; /* only used in rx_buffer */
+-
+- struct r3964_block_header *next;
+- struct r3964_client_info *owner; /* =NULL in rx_buffer */
+-};
+-
+-/*
+- * If rx_buf hasn't enough space to store R3964_MTU chars,
+- * we will reject all incoming STX-requests by sending NAK.
+- */
+-
+-#define RX_BUF_SIZE 4000
+-#define TX_BUF_SIZE 4000
+-#define R3964_MAX_BLOCKS_IN_RX_QUEUE 100
+-
+-#define R3964_PARITY 0x0001
+-#define R3964_FRAME 0x0002
+-#define R3964_OVERRUN 0x0004
+-#define R3964_UNKNOWN 0x0008
+-#define R3964_BREAK 0x0010
+-#define R3964_CHECKSUM 0x0020
+-#define R3964_ERROR 0x003f
+-#define R3964_BCC 0x4000
+-#define R3964_DEBUG 0x8000
+-
+-
+-struct r3964_info {
+- spinlock_t lock;
+- struct tty_struct *tty;
+- unsigned char priority;
+- unsigned char *rx_buf; /* ring buffer */
+- unsigned char *tx_buf;
+-
+- struct r3964_block_header *rx_first;
+- struct r3964_block_header *rx_last;
+- struct r3964_block_header *tx_first;
+- struct r3964_block_header *tx_last;
+- unsigned int tx_position;
+- unsigned int rx_position;
+- unsigned char last_rx;
+- unsigned char bcc;
+- unsigned int blocks_in_rx_queue;
+-
+- struct mutex read_lock; /* serialize read and ioctl */
+-
+- struct r3964_client_info *firstClient;
+- unsigned int state;
+- unsigned int flags;
+-
+- struct timer_list tmr;
+- int nRetry;
+-};
+-
+-#endif
+--
+2.21.0
+
diff --git a/0004-tty-n_r3964-drop-ancient-header-changelog-text.patch b/0004-tty-n_r3964-drop-ancient-header-changelog-text.patch
new file mode 100644
index 00000000000000..46c61661f16528
--- /dev/null
+++ b/0004-tty-n_r3964-drop-ancient-header-changelog-text.patch
@@ -0,0 +1,74 @@
+From d01e4fbee328ad33555c740d7881129fe6d15175 Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Wed, 23 Jan 2019 20:31:02 +0100
+Subject: [PATCH 04/15] tty: n_r3964: drop ancient header changelog text
+
+No need to keep changelog text from 2001 and earlier in the file itself,
+drop it all. This keeps the original copyright and author information
+in the file.
+
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 45 ++++---------------------------------------
+ 1 file changed, 4 insertions(+), 41 deletions(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index 62a0e869eab5..6c0abb04c4f0 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -1,48 +1,11 @@
+ // SPDX-License-Identifier: GPL-1.0+
+-/* r3964 linediscipline for linux
++/*
++ * r3964 line discipline
+ *
+- * -----------------------------------------------------------
+- * Copyright by
+- * Philips Automation Projects
++ * Copyright by Philips Automation Projects
+ * Kassel (Germany)
+- * -----------------------------------------------------------
+- * Author:
+- * L. Haag
+- *
+- * $Log: n_r3964.c,v $
+- * Revision 1.10 2001/03/18 13:02:24 dwmw2
+- * Fix timer usage, use spinlocks properly.
+- *
+- * Revision 1.9 2001/03/18 12:52:14 dwmw2
+- * Merge changes in 2.4.2
+- *
+- * Revision 1.8 2000/03/23 14:14:54 dwmw2
+- * Fix race in sleeping in r3964_read()
+- *
+- * Revision 1.7 1999/28/08 11:41:50 dwmw2
+- * Port to 2.3 kernel
+- *
+- * Revision 1.6 1998/09/30 00:40:40 dwmw2
+- * Fixed compilation on 2.0.x kernels
+- * Updated to newly registered tty-ldisc number 9
+- *
+- * Revision 1.5 1998/09/04 21:57:36 dwmw2
+- * Signal handling bug fixes, port to 2.1.x.
+- *
+- * Revision 1.4 1998/04/02 20:26:59 lhaag
+- * select, blocking, ...
+- *
+- * Revision 1.3 1998/02/12 18:58:43 root
+- * fixed some memory leaks
+- * calculation of checksum characters
+- *
+- * Revision 1.2 1998/02/07 13:03:34 root
+- * ioctl read_telegram
+- *
+- * Revision 1.1 1998/02/06 19:21:03 root
+- * Initial revision
+- *
+ *
++ * Author: L. Haag
+ */
+
+ #include <linux/module.h>
+--
+2.21.0
+
diff --git a/0005-tty-n_r3964-split-rx-and-tx-header-structures.patch b/0005-tty-n_r3964-split-rx-and-tx-header-structures.patch
new file mode 100644
index 00000000000000..0f633183531e36
--- /dev/null
+++ b/0005-tty-n_r3964-split-rx-and-tx-header-structures.patch
@@ -0,0 +1,265 @@
+From b717816885270bfe80ed184cb894b14c10c3f48f Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Thu, 24 Jan 2019 12:32:05 +0100
+Subject: [PATCH 05/15] tty: n_r3964: split rx and tx header structures
+
+It's really confusing to try to figure out what structure is what type
+of header when both the tx and rx queues are using the same header
+structure, but not all of the fields in it.
+
+So split this into two different structures. That makes it much more
+obvious what variable and queue and type of message is being kept track
+of where.
+
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 78 +++++++++++++++++++++----------------------
+ 1 file changed, 39 insertions(+), 39 deletions(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index 6c0abb04c4f0..487aa6a38f7d 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -101,6 +101,8 @@ 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;
+
+ struct r3964_client_info {
+ spinlock_t lock;
+@@ -111,30 +113,35 @@ struct r3964_client_info {
+
+ struct r3964_message *first_msg;
+ struct r3964_message *last_msg;
+- struct r3964_block_header *next_block_to_read;
++ struct rx_block_header *next_block_to_read;
+ int msg_count;
+ };
+
+-struct r3964_block_header;
+-
+ /* internal version of client_message: */
+ struct r3964_message {
+ int msg_id;
+ int arg;
+ int error_code;
+- struct r3964_block_header *block;
++ struct rx_block_header *block;
+ struct r3964_message *next;
+ };
+
+-/* Header of received block in rx_buf/tx_buf: */
+-struct r3964_block_header {
++/* Header of received block in rx_buf: */
++struct rx_block_header {
+ unsigned int length; /* length in chars without header */
+ unsigned char *data; /* usually data is located immediately
+ * behind this struct */
+ unsigned int locks; /* only used in rx_buffer */
+
+- struct r3964_block_header *next;
+- struct r3964_client_info *owner; /* =NULL in rx_buffer */
++ struct rx_block_header *next;
++};
++
++/* Header of received block in tx_buf: */
++struct tx_block_header {
++ unsigned int length; /* length in chars without header */
++ unsigned char *data;
++ struct tx_block_header *next;
++ struct r3964_client_info *owner;
+ };
+
+ /*
+@@ -162,10 +169,10 @@ struct r3964_info {
+ unsigned char *rx_buf; /* ring buffer */
+ unsigned char *tx_buf;
+
+- struct r3964_block_header *rx_first;
+- struct r3964_block_header *rx_last;
+- struct r3964_block_header *tx_first;
+- struct r3964_block_header *tx_last;
++ struct rx_block_header *rx_first;
++ struct rx_block_header *rx_last;
++ struct tx_block_header *tx_first;
++ struct tx_block_header *tx_last;
+ unsigned int tx_position;
+ unsigned int rx_position;
+ unsigned char last_rx;
+@@ -182,8 +189,6 @@ struct r3964_info {
+ int nRetry;
+ };
+
+-static void add_tx_queue(struct r3964_info *, struct r3964_block_header *);
+-static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code);
+ static void put_char(struct r3964_info *pInfo, unsigned char ch);
+ static void trigger_transmit(struct r3964_info *pInfo);
+ static void retry_transmit(struct r3964_info *pInfo);
+@@ -195,7 +200,7 @@ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg);
+ static int read_telegram(struct r3964_info *pInfo, struct pid *pid,
+ unsigned char __user * buf);
+ static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg,
+- int error_code, struct r3964_block_header *pBlock);
++ int error_code, struct rx_block_header *pBlock);
+ static struct r3964_message *remove_msg(struct r3964_info *pInfo,
+ struct r3964_client_info *pClient);
+ static void remove_client_block(struct r3964_info *pInfo,
+@@ -306,7 +311,7 @@ module_exit(r3964_exit);
+ *************************************************************/
+
+ static void add_tx_queue(struct r3964_info *pInfo,
+- struct r3964_block_header *pHeader)
++ struct tx_block_header *pHeader)
+ {
+ unsigned long flags;
+
+@@ -329,10 +334,10 @@ static void add_tx_queue(struct r3964_info *pInfo,
+
+ static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code)
+ {
+- struct r3964_block_header *pHeader;
++ struct tx_block_header *pHeader;
+ unsigned long flags;
+ #ifdef DEBUG_QUEUE
+- struct r3964_block_header *pDump;
++ struct tx_block_header *pDump;
+ #endif
+
+ pHeader = pInfo->tx_first;
+@@ -376,7 +381,7 @@ static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code)
+ }
+
+ static void add_rx_queue(struct r3964_info *pInfo,
+- struct r3964_block_header *pHeader)
++ struct rx_block_header *pHeader)
+ {
+ unsigned long flags;
+
+@@ -400,10 +405,10 @@ static void add_rx_queue(struct r3964_info *pInfo,
+ }
+
+ static void remove_from_rx_queue(struct r3964_info *pInfo,
+- struct r3964_block_header *pHeader)
++ struct rx_block_header *pHeader)
+ {
+ unsigned long flags;
+- struct r3964_block_header *pFind;
++ struct rx_block_header *pFind;
+
+ if (pHeader == NULL)
+ return;
+@@ -517,7 +522,7 @@ static void retry_transmit(struct r3964_info *pInfo)
+ static void transmit_block(struct r3964_info *pInfo)
+ {
+ struct tty_struct *tty = pInfo->tty;
+- struct r3964_block_header *pBlock = pInfo->tx_first;
++ struct tx_block_header *pBlock = pInfo->tx_first;
+ int room = 0;
+
+ if (tty == NULL || pBlock == NULL) {
+@@ -558,7 +563,7 @@ static void on_receive_block(struct r3964_info *pInfo)
+ {
+ unsigned int length;
+ struct r3964_client_info *pClient;
+- struct r3964_block_header *pBlock;
++ struct rx_block_header *pBlock;
+ unsigned long flags;
+
+ length = pInfo->rx_position;
+@@ -596,20 +601,17 @@ static void on_receive_block(struct r3964_info *pInfo)
+ del_timer_sync(&pInfo->tmr);
+ TRACE_PS(" rx success: got %d chars", length);
+
+- /* prepare struct r3964_block_header: */
+- pBlock = kmalloc(length + sizeof(struct r3964_block_header),
+- GFP_KERNEL);
++ /* prepare struct rx_block_header: */
++ pBlock = kmalloc(length + sizeof(*pBlock), GFP_KERNEL);
+ TRACE_M("on_receive_block - kmalloc %p", pBlock);
+
+ if (pBlock == NULL)
+ return;
+
+ pBlock->length = length;
+- pBlock->data = ((unsigned char *)pBlock) +
+- sizeof(struct r3964_block_header);
++ pBlock->data = ((unsigned char *)pBlock) + sizeof(*pBlock);
+ pBlock->locks = 0;
+ pBlock->next = NULL;
+- pBlock->owner = NULL;
+
+ memcpy(pBlock->data, pInfo->rx_buf, length);
+
+@@ -905,7 +907,7 @@ static int read_telegram(struct r3964_info *pInfo, struct pid *pid,
+ unsigned char __user * buf)
+ {
+ struct r3964_client_info *pClient;
+- struct r3964_block_header *block;
++ struct rx_block_header *block;
+
+ if (!buf) {
+ return -EINVAL;
+@@ -931,7 +933,7 @@ static int read_telegram(struct r3964_info *pInfo, struct pid *pid,
+ }
+
+ static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg,
+- int error_code, struct r3964_block_header *pBlock)
++ int error_code, struct rx_block_header *pBlock)
+ {
+ struct r3964_message *pMsg;
+ unsigned long flags;
+@@ -1014,7 +1016,7 @@ static struct r3964_message *remove_msg(struct r3964_info *pInfo,
+ static void remove_client_block(struct r3964_info *pInfo,
+ struct r3964_client_info *pClient)
+ {
+- struct r3964_block_header *block;
++ struct rx_block_header *block;
+
+ TRACE_PS("remove_client_block PID %d", pid_nr(pClient->pid));
+
+@@ -1098,7 +1100,7 @@ static void r3964_close(struct tty_struct *tty)
+ struct r3964_info *pInfo = tty->disc_data;
+ struct r3964_client_info *pClient, *pNext;
+ struct r3964_message *pMsg;
+- struct r3964_block_header *pHeader, *pNextHeader;
++ struct tx_block_header *pHeader, *pNextHeader;
+ unsigned long flags;
+
+ TRACE_L("close");
+@@ -1214,7 +1216,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
+ const unsigned char *data, size_t count)
+ {
+ struct r3964_info *pInfo = tty->disc_data;
+- struct r3964_block_header *pHeader;
++ struct tx_block_header *pHeader;
+ struct r3964_client_info *pClient;
+ unsigned char *new_data;
+
+@@ -1239,8 +1241,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
+ /*
+ * Allocate a buffer for the data and copy it from the buffer with header prepended
+ */
+- new_data = kmalloc(count + sizeof(struct r3964_block_header),
+- GFP_KERNEL);
++ new_data = kmalloc(count + sizeof(*pHeader), GFP_KERNEL);
+ TRACE_M("r3964_write - kmalloc %p", new_data);
+ if (new_data == NULL) {
+ if (pInfo->flags & R3964_DEBUG) {
+@@ -1249,10 +1250,9 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
+ return -ENOSPC;
+ }
+
+- pHeader = (struct r3964_block_header *)new_data;
+- pHeader->data = new_data + sizeof(struct r3964_block_header);
++ pHeader = (struct tx_block_header *)new_data;
++ pHeader->data = new_data + sizeof(*pHeader);
+ pHeader->length = count;
+- pHeader->locks = 0;
+ pHeader->owner = NULL;
+
+ pClient = findClient(pInfo, task_pid(current));
+--
+2.21.0
+
diff --git a/0006-tty-n_r3964-for-tx_blocks-use-a-real-kernel-list.patch b/0006-tty-n_r3964-for-tx_blocks-use-a-real-kernel-list.patch
new file mode 100644
index 00000000000000..5dacea8486862b
--- /dev/null
+++ b/0006-tty-n_r3964-for-tx_blocks-use-a-real-kernel-list.patch
@@ -0,0 +1,187 @@
+From 55124901b0e4b05999669830cf6e0b240f8cb2fd Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Thu, 24 Jan 2019 13:35:36 +0100
+Subject: [PATCH 06/15] tty: n_r3964: for tx_blocks, use a real kernel list
+
+The tx blocks have a hand-rolled linked list structure, use a "normal"
+kernel list structure instead, making the code smaller and easier to
+understand and verify that it really does what we think it should be
+doing.
+
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 85 +++++++++++++------------------------------
+ 1 file changed, 26 insertions(+), 59 deletions(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index 487aa6a38f7d..4a9750adcc56 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -140,7 +140,7 @@ struct rx_block_header {
+ struct tx_block_header {
+ unsigned int length; /* length in chars without header */
+ unsigned char *data;
+- struct tx_block_header *next;
++ struct list_head node;
+ struct r3964_client_info *owner;
+ };
+
+@@ -171,8 +171,7 @@ struct r3964_info {
+
+ struct rx_block_header *rx_first;
+ struct rx_block_header *rx_last;
+- struct tx_block_header *tx_first;
+- struct tx_block_header *tx_last;
++ struct list_head tx_blocks;
+ unsigned int tx_position;
+ unsigned int rx_position;
+ unsigned char last_rx;
+@@ -316,42 +315,25 @@ static void add_tx_queue(struct r3964_info *pInfo,
+ unsigned long flags;
+
+ spin_lock_irqsave(&pInfo->lock, flags);
+-
+- pHeader->next = NULL;
+-
+- if (pInfo->tx_last == NULL) {
+- pInfo->tx_first = pInfo->tx_last = pHeader;
+- } else {
+- pInfo->tx_last->next = pHeader;
+- pInfo->tx_last = pHeader;
+- }
+-
++ list_add_tail(&pHeader->node, &pInfo->tx_blocks);
+ spin_unlock_irqrestore(&pInfo->lock, flags);
+-
+- TRACE_Q("add_tx_queue %p, length %d, tx_first = %p",
+- pHeader, pHeader->length, pInfo->tx_first);
+ }
+
+ static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code)
+ {
+ struct tx_block_header *pHeader;
+ unsigned long flags;
+-#ifdef DEBUG_QUEUE
+- struct tx_block_header *pDump;
+-#endif
+
+- pHeader = pInfo->tx_first;
+-
+- if (pHeader == NULL)
++ spin_lock_irqsave(&pInfo->lock, flags);
++ if (list_empty(&pInfo->tx_blocks)) {
++ spin_unlock_irqrestore(&pInfo->lock, flags);
+ return;
++ }
+
+-#ifdef DEBUG_QUEUE
+- printk("r3964: remove_from_tx_queue: %p, length %u - ",
+- pHeader, pHeader->length);
+- for (pDump = pHeader; pDump; pDump = pDump->next)
+- printk("%p ", pDump);
+- printk("\n");
+-#endif
++ pHeader = list_first_entry(&pInfo->tx_blocks, struct tx_block_header,
++ node);
++ list_del(&pHeader->node);
++ spin_unlock_irqrestore(&pInfo->lock, flags);
+
+ if (pHeader->owner) {
+ if (error_code) {
+@@ -364,20 +346,7 @@ static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code)
+ wake_up_interruptible(&pInfo->tty->read_wait);
+ }
+
+- spin_lock_irqsave(&pInfo->lock, flags);
+-
+- pInfo->tx_first = pHeader->next;
+- if (pInfo->tx_first == NULL) {
+- pInfo->tx_last = NULL;
+- }
+-
+- spin_unlock_irqrestore(&pInfo->lock, flags);
+-
+ kfree(pHeader);
+- TRACE_M("remove_from_tx_queue - kfree %p", pHeader);
+-
+- TRACE_Q("remove_from_tx_queue: tx_first = %p, tx_last = %p",
+- pInfo->tx_first, pInfo->tx_last);
+ }
+
+ static void add_rx_queue(struct r3964_info *pInfo,
+@@ -476,7 +445,7 @@ static void trigger_transmit(struct r3964_info *pInfo)
+
+ spin_lock_irqsave(&pInfo->lock, flags);
+
+- if ((pInfo->state == R3964_IDLE) && (pInfo->tx_first != NULL)) {
++ if ((pInfo->state == R3964_IDLE) && (!list_empty(&pInfo->tx_blocks))) {
+ pInfo->state = R3964_TX_REQUEST;
+ pInfo->nRetry = 0;
+ pInfo->flags &= ~R3964_ERROR;
+@@ -522,17 +491,19 @@ static void retry_transmit(struct r3964_info *pInfo)
+ static void transmit_block(struct r3964_info *pInfo)
+ {
+ struct tty_struct *tty = pInfo->tty;
+- struct tx_block_header *pBlock = pInfo->tx_first;
+- int room = 0;
++ struct tx_block_header *pBlock;
++ int room;
+
+- if (tty == NULL || pBlock == NULL) {
++ if (!tty)
+ return;
+- }
+
+- room = tty_write_room(tty);
++ if (list_empty(&pInfo->tx_blocks))
++ return;
++
++ pBlock = list_first_entry(&pInfo->tx_blocks, struct tx_block_header,
++ node);
+
+- TRACE_PS("transmit_block %p, room %d, length %d",
+- pBlock, room, pBlock->length);
++ room = tty_write_room(tty);
+
+ while (pInfo->tx_position < pBlock->length) {
+ if (room < 2)
+@@ -1077,7 +1048,7 @@ static int r3964_open(struct tty_struct *tty)
+ pInfo->tty = tty;
+ pInfo->priority = R3964_MASTER;
+ pInfo->rx_first = pInfo->rx_last = NULL;
+- pInfo->tx_first = pInfo->tx_last = NULL;
++ INIT_LIST_HEAD(&pInfo->tx_blocks);
+ pInfo->rx_position = 0;
+ pInfo->tx_position = 0;
+ pInfo->last_rx = 0;
+@@ -1100,7 +1071,7 @@ static void r3964_close(struct tty_struct *tty)
+ struct r3964_info *pInfo = tty->disc_data;
+ struct r3964_client_info *pClient, *pNext;
+ struct r3964_message *pMsg;
+- struct tx_block_header *pHeader, *pNextHeader;
++ struct tx_block_header *pHeader, *tmp;
+ unsigned long flags;
+
+ TRACE_L("close");
+@@ -1129,15 +1100,11 @@ static void r3964_close(struct tty_struct *tty)
+ }
+ /* Remove jobs from tx_queue: */
+ spin_lock_irqsave(&pInfo->lock, flags);
+- pHeader = pInfo->tx_first;
+- pInfo->tx_first = pInfo->tx_last = NULL;
+- spin_unlock_irqrestore(&pInfo->lock, flags);
+-
+- while (pHeader) {
+- pNextHeader = pHeader->next;
++ list_for_each_entry_safe(pHeader, tmp, &pInfo->tx_blocks, node) {
++ list_del(&pHeader->node);
+ kfree(pHeader);
+- pHeader = pNextHeader;
+ }
++ spin_unlock_irqrestore(&pInfo->lock, flags);
+
+ /* Free buffers: */
+ kfree(pInfo->rx_buf);
+--
+2.21.0
+
diff --git a/0007-tty-n_r3964-for-rx_blocks-use-a-real-kernel-list.patch b/0007-tty-n_r3964-for-rx_blocks-use-a-real-kernel-list.patch
new file mode 100644
index 00000000000000..749651594132ab
--- /dev/null
+++ b/0007-tty-n_r3964-for-rx_blocks-use-a-real-kernel-list.patch
@@ -0,0 +1,141 @@
+From c711e9d95e2fbc99dc88c0797f58194ade169258 Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Thu, 24 Jan 2019 16:04:54 +0100
+Subject: [PATCH 07/15] tty: n_r3964: for rx_blocks, use a real kernel list
+
+The rx blocks have a hand-rolled linked list structure, use a "normal"
+kernel list structure instead, making the code smaller and easier to
+understand and verify that it really does what we think it should be
+doing.
+
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 67 +++++++++----------------------------------
+ 1 file changed, 13 insertions(+), 54 deletions(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index 4a9750adcc56..8275fb905aeb 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -132,8 +132,7 @@ struct rx_block_header {
+ unsigned char *data; /* usually data is located immediately
+ * behind this struct */
+ unsigned int locks; /* only used in rx_buffer */
+-
+- struct rx_block_header *next;
++ struct list_head node;
+ };
+
+ /* Header of received block in tx_buf: */
+@@ -169,8 +168,7 @@ struct r3964_info {
+ unsigned char *rx_buf; /* ring buffer */
+ unsigned char *tx_buf;
+
+- struct rx_block_header *rx_first;
+- struct rx_block_header *rx_last;
++ struct list_head rx_blocks;
+ struct list_head tx_blocks;
+ unsigned int tx_position;
+ unsigned int rx_position;
+@@ -355,71 +353,32 @@ static void add_rx_queue(struct r3964_info *pInfo,
+ unsigned long flags;
+
+ spin_lock_irqsave(&pInfo->lock, flags);
+-
+- pHeader->next = NULL;
+-
+- if (pInfo->rx_last == NULL) {
+- pInfo->rx_first = pInfo->rx_last = pHeader;
+- } else {
+- pInfo->rx_last->next = pHeader;
+- pInfo->rx_last = pHeader;
+- }
+- pInfo->blocks_in_rx_queue++;
+-
++ list_add_tail(&pHeader->node, &pInfo->rx_blocks);
+ spin_unlock_irqrestore(&pInfo->lock, flags);
+-
+- TRACE_Q("add_rx_queue: %p, length = %d, rx_first = %p, count = %d",
+- pHeader, pHeader->length,
+- pInfo->rx_first, pInfo->blocks_in_rx_queue);
+ }
+
+ static void remove_from_rx_queue(struct r3964_info *pInfo,
+ struct rx_block_header *pHeader)
+ {
++ struct rx_block_header *pFind, *tmp;
+ unsigned long flags;
+- struct rx_block_header *pFind;
+
+ if (pHeader == NULL)
+ return;
+
+- TRACE_Q("remove_from_rx_queue: rx_first = %p, rx_last = %p, count = %d",
+- pInfo->rx_first, pInfo->rx_last, pInfo->blocks_in_rx_queue);
+- TRACE_Q("remove_from_rx_queue: %p, length %u",
+- pHeader, pHeader->length);
+-
+ spin_lock_irqsave(&pInfo->lock, flags);
+-
+- if (pInfo->rx_first == pHeader) {
+- /* Remove the first block in the linked list: */
+- pInfo->rx_first = pHeader->next;
+-
+- if (pInfo->rx_first == NULL) {
+- pInfo->rx_last = NULL;
+- }
+- pInfo->blocks_in_rx_queue--;
+- } else {
+- /* Find block to remove: */
+- for (pFind = pInfo->rx_first; pFind; pFind = pFind->next) {
+- if (pFind->next == pHeader) {
+- /* Got it. */
+- pFind->next = pHeader->next;
+- pInfo->blocks_in_rx_queue--;
+- if (pFind->next == NULL) {
+- /* Oh, removed the last one! */
+- pInfo->rx_last = pFind;
+- }
+- break;
+- }
++ list_for_each_entry_safe(pFind, tmp, &pInfo->rx_blocks, node) {
++ if (pFind == pHeader) {
++ /* Got it. */
++ list_del(&pFind->node);
++ pInfo->blocks_in_rx_queue--;
++ goto exit;
+ }
+ }
+-
++exit:
+ spin_unlock_irqrestore(&pInfo->lock, flags);
+
+ kfree(pHeader);
+- TRACE_M("remove_from_rx_queue - kfree %p", pHeader);
+-
+- TRACE_Q("remove_from_rx_queue: rx_first = %p, rx_last = %p, count = %d",
+- pInfo->rx_first, pInfo->rx_last, pInfo->blocks_in_rx_queue);
+ }
+
+ static void put_char(struct r3964_info *pInfo, unsigned char ch)
+@@ -582,7 +541,7 @@ static void on_receive_block(struct r3964_info *pInfo)
+ pBlock->length = length;
+ pBlock->data = ((unsigned char *)pBlock) + sizeof(*pBlock);
+ pBlock->locks = 0;
+- pBlock->next = NULL;
++ INIT_LIST_HEAD(&pBlock->node);
+
+ memcpy(pBlock->data, pInfo->rx_buf, length);
+
+@@ -1047,7 +1006,7 @@ static int r3964_open(struct tty_struct *tty)
+ mutex_init(&pInfo->read_lock);
+ pInfo->tty = tty;
+ pInfo->priority = R3964_MASTER;
+- pInfo->rx_first = pInfo->rx_last = NULL;
++ INIT_LIST_HEAD(&pInfo->rx_blocks);
+ INIT_LIST_HEAD(&pInfo->tx_blocks);
+ pInfo->rx_position = 0;
+ pInfo->tx_position = 0;
+--
+2.21.0
+
diff --git a/0008-tty-n_r3964-don-t-hand-roll-a-reference-count.patch b/0008-tty-n_r3964-don-t-hand-roll-a-reference-count.patch
new file mode 100644
index 00000000000000..0fd59803294b63
--- /dev/null
+++ b/0008-tty-n_r3964-don-t-hand-roll-a-reference-count.patch
@@ -0,0 +1,160 @@
+From 6e885d94ba582aec3e689d94b3b2deb3570a5e06 Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Thu, 24 Jan 2019 16:44:28 +0100
+Subject: [PATCH 08/15] tty: n_r3964: don't hand-roll a reference count
+
+rx_block_header had a "locks" variable that was trying to be a reference
+count on the header. When it would drop to zero, the memory would be
+freed. Convert this to be a kref instead to handle the housekeeping for
+doing reference counting properly.
+
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 61 ++++++++++++++++++++-----------------------
+ 1 file changed, 28 insertions(+), 33 deletions(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index 8275fb905aeb..a79c8d485030 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -26,6 +26,7 @@
+ #include <linux/param.h>
+ #include <linux/poll.h>
+ #include <linux/init.h>
++#include <linux/kref.h>
+ #include <linux/uaccess.h>
+ #include <uapi/linux/n_r3964.h>
+
+@@ -131,8 +132,9 @@ struct rx_block_header {
+ unsigned int length; /* length in chars without header */
+ unsigned char *data; /* usually data is located immediately
+ * behind this struct */
+- unsigned int locks; /* only used in rx_buffer */
+ struct list_head node;
++ struct kref kref;
++ struct r3964_info *info;
+ };
+
+ /* Header of received block in tx_buf: */
+@@ -200,8 +202,7 @@ 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 void remove_client_block(struct r3964_info *pInfo,
+- struct r3964_client_info *pClient);
++static void remove_client_block(struct r3964_client_info *pClient);
+
+ static int r3964_open(struct tty_struct *tty);
+ static void r3964_close(struct tty_struct *tty);
+@@ -357,28 +358,28 @@ static void add_rx_queue(struct r3964_info *pInfo,
+ spin_unlock_irqrestore(&pInfo->lock, flags);
+ }
+
+-static void remove_from_rx_queue(struct r3964_info *pInfo,
+- struct rx_block_header *pHeader)
++static void remove_from_rx_queue(struct kref *kref)
+ {
+- struct rx_block_header *pFind, *tmp;
++ struct rx_block_header *header, *find;
++ struct r3964_info *info;
+ unsigned long flags;
+
+- if (pHeader == NULL)
+- return;
++ header = container_of(kref, struct rx_block_header, kref);
++ info = header->info;
+
+- spin_lock_irqsave(&pInfo->lock, flags);
+- list_for_each_entry_safe(pFind, tmp, &pInfo->rx_blocks, node) {
+- if (pFind == pHeader) {
++ spin_lock_irqsave(&info->lock, flags);
++ list_for_each_entry(find, &info->rx_blocks, node) {
++ if (find == header) {
+ /* Got it. */
+- list_del(&pFind->node);
+- pInfo->blocks_in_rx_queue--;
++ list_del(&find->node);
++ info->blocks_in_rx_queue--;
+ goto exit;
+ }
+ }
+ exit:
+- spin_unlock_irqrestore(&pInfo->lock, flags);
++ spin_unlock_irqrestore(&info->lock, flags);
+
+- kfree(pHeader);
++ kfree(header);
+ }
+
+ static void put_char(struct r3964_info *pInfo, unsigned char ch)
+@@ -540,7 +541,8 @@ static void on_receive_block(struct r3964_info *pInfo)
+
+ pBlock->length = length;
+ pBlock->data = ((unsigned char *)pBlock) + sizeof(*pBlock);
+- pBlock->locks = 0;
++ pBlock->info = pInfo;
++ kref_init(&pBlock->kref);
+ INIT_LIST_HEAD(&pBlock->node);
+
+ memcpy(pBlock->data, pInfo->rx_buf, length);
+@@ -855,7 +857,7 @@ static int read_telegram(struct r3964_info *pInfo, struct pid *pid,
+ if (copy_to_user(buf, block->data, block->length))
+ return -EFAULT;
+
+- remove_client_block(pInfo, pClient);
++ remove_client_block(pClient);
+ return block->length;
+ }
+
+@@ -894,9 +896,9 @@ static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg,
+
+ pClient->msg_count++;
+
+- if (pBlock != NULL) {
+- pBlock->locks++;
+- }
++ if (pBlock != NULL)
++ kref_get(&pBlock->kref);
++
+ spin_unlock_irqrestore(&pClient->lock, flags);
+ } else {
+ if ((pClient->last_msg->msg_id == R3964_MSG_ACK)
+@@ -935,7 +937,7 @@ static struct r3964_message *remove_msg(struct r3964_info *pInfo,
+
+ pClient->msg_count--;
+ if (pMsg->block) {
+- remove_client_block(pInfo, pClient);
++ remove_client_block(pClient);
+ pClient->next_block_to_read = pMsg->block;
+ }
+ spin_unlock_irqrestore(&pClient->lock, flags);
+@@ -943,21 +945,14 @@ static struct r3964_message *remove_msg(struct r3964_info *pInfo,
+ return pMsg;
+ }
+
+-static void remove_client_block(struct r3964_info *pInfo,
+- struct r3964_client_info *pClient)
++static void remove_client_block(struct r3964_client_info *client)
+ {
+ struct rx_block_header *block;
+
+- TRACE_PS("remove_client_block PID %d", pid_nr(pClient->pid));
+-
+- block = pClient->next_block_to_read;
+- if (block) {
+- block->locks--;
+- if (block->locks == 0) {
+- remove_from_rx_queue(pInfo, block);
+- }
+- }
+- pClient->next_block_to_read = NULL;
++ block = client->next_block_to_read;
++ if (block)
++ kref_put(&block->kref, remove_from_rx_queue);
++ client->next_block_to_read = NULL;
+ }
+
+ /*************************************************************
+--
+2.21.0
+
diff --git a/0009-tty-n_r3964-for-messages-use-a-real-kernel-list.patch b/0009-tty-n_r3964-for-messages-use-a-real-kernel-list.patch
new file mode 100644
index 00000000000000..bafeacf154ba65
--- /dev/null
+++ b/0009-tty-n_r3964-for-messages-use-a-real-kernel-list.patch
@@ -0,0 +1,268 @@
+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 | 132 ++++++++++++++++++++----------------------
+ 1 file changed, 62 insertions(+), 70 deletions(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index a79c8d485030..cf2a3dc3a870 100644
+--- 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_info *pInfo, struct pid *pid,
+ 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_info *pInfo, struct pid *pid, int arg)
+ 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_info *pInfo, struct pid *pid, int arg)
+ 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_info *pInfo, struct pid *pid,
+ 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);
++ if (pMsg == NULL)
++ return;
+
+- pMsg = kmalloc(sizeof(*pMsg), GFP_ATOMIC);
+- TRACE_M("add_msg - kmalloc %p", pMsg);
+- if (pMsg == NULL) {
+- return;
+- }
++ pMsg->msg_id = msg_id;
++ pMsg->arg = arg;
++ pMsg->error_code = error_code;
++ pMsg->block = pBlock;
++ INIT_LIST_HEAD(&pMsg->node);
+
+- spin_lock_irqsave(&pClient->lock, flags);
++ list_add_tail(&pMsg->node, &pClient->msgs);
++ pClient->msg_count++;
+
+- pMsg->msg_id = msg_id;
+- pMsg->arg = arg;
+- pMsg->error_code = error_code;
+- pMsg->block = pBlock;
+- pMsg->next = NULL;
+-
+- 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);
++ spin_lock_irqsave(&client->lock, flags);
++ if (list_empty(&client->msgs)) {
++ spin_unlock_irqrestore(&client->lock, flags);
++ return NULL;
++ }
+
+- pMsg = pClient->first_msg;
+- pClient->first_msg = pMsg->next;
+- if (pClient->first_msg == NULL) {
+- pClient->last_msg = NULL;
+- }
++ msg = list_first_entry(&client->msgs, struct r3964_message, node);
++ list_del(&msg->node);
+
+- pClient->msg_count--;
+- if (pMsg->block) {
+- remove_client_block(pClient);
+- pClient->next_block_to_read = pMsg->block;
+- }
+- spin_unlock_irqrestore(&pClient->lock, flags);
++ 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_struct *tty)
+ 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_struct *tty, struct file *file,
+
+ 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_struct *tty, struct file *file,
+ }
+ /* 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_struct *tty, struct file *file,
+ {
+ 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_struct *tty, struct file *file,
+ 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;
+ }
+--
+2.21.0
+
diff --git a/0010-tty-n_r3964-for-clients-use-a-real-kernel-list.patch b/0010-tty-n_r3964-for-clients-use-a-real-kernel-list.patch
new file mode 100644
index 00000000000000..de4cc61f1ed61a
--- /dev/null
+++ b/0010-tty-n_r3964-for-clients-use-a-real-kernel-list.patch
@@ -0,0 +1,148 @@
+From 336b06e9db65482b6146e112670cff7e4e52c7ef Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Fri, 25 Jan 2019 09:35:45 +0100
+Subject: [PATCH 10/15] tty: n_r3964: for clients, use a real kernel list
+
+The "info" structure had a hand-rolled linked list of all of the clients
+associated with it. Convert that over to a kernel list structure,
+making the logic a lot simpler and easier to understand.
+
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 31 ++++++++++++-------------------
+ 1 file changed, 12 insertions(+), 19 deletions(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index cf2a3dc3a870..f752eec92448 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -109,8 +109,7 @@ struct r3964_client_info {
+ struct pid *pid;
+ unsigned int sig_flags;
+
+- struct r3964_client_info *next;
+-
++ struct list_head node;
+ struct list_head msgs;
+ struct rx_block_header *next_block_to_read;
+ int msg_count;
+@@ -178,7 +177,7 @@ struct r3964_info {
+
+ struct mutex read_lock; /* serialize read and ioctl */
+
+- struct r3964_client_info *firstClient;
++ struct list_head clients;
+ unsigned int state;
+ unsigned int flags;
+
+@@ -549,7 +548,7 @@ static void on_receive_block(struct r3964_info *pInfo)
+
+ /* notify attached client processes: */
+ spin_lock_irqsave(&pInfo->lock, flags);
+- for (pClient = pInfo->firstClient; pClient; pClient = pClient->next) {
++ list_for_each_entry(pClient, &pInfo->clients, node) {
+ if (pClient->sig_flags & R3964_SIG_DATA) {
+ add_msg(pClient, R3964_MSG_DATA, length, R3964_OK,
+ pBlock);
+@@ -755,11 +754,12 @@ static struct r3964_client_info *findClient(struct r3964_info *pInfo,
+ unsigned long flags;
+
+ spin_lock_irqsave(&pInfo->lock, flags);
+- for (pClient = pInfo->firstClient; pClient; pClient = pClient->next) {
++ list_for_each_entry(pClient, &pInfo->clients, node) {
+ if (pClient->pid == pid) {
+ goto exit;
+ }
+ }
++ pClient = NULL;
+ exit:
+ spin_unlock_irqrestore(&pInfo->lock, flags);
+ return pClient;
+@@ -768,7 +768,6 @@ static struct r3964_client_info *findClient(struct r3964_info *pInfo,
+ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+ {
+ struct r3964_client_info *pClient;
+- struct r3964_client_info **ppClient;
+ struct r3964_message *pMsg;
+ unsigned long flags;
+
+@@ -776,14 +775,10 @@ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+ spin_lock_irqsave(&pInfo->lock, flags);
+
+ /* Remove client from client list */
+- for (ppClient = &pInfo->firstClient; *ppClient;
+- ppClient = &(*ppClient)->next) {
+- pClient = *ppClient;
+-
++ list_for_each_entry(pClient, &pInfo->clients, node) {
+ if (pClient->pid == pid) {
+ TRACE_PS("removing client %d from client list",
+ pid_nr(pid));
+- *ppClient = pClient->next;
+ while (pClient->msg_count) {
+ pMsg = remove_msg(pClient);
+ if (pMsg) {
+@@ -793,6 +788,7 @@ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+ }
+ }
+ put_pid(pClient->pid);
++ list_del(&pClient->node);
+ kfree(pClient);
+ TRACE_M("enable_signals - kfree %p", pClient);
+ spin_unlock_irqrestore(&pInfo->lock, flags);
+@@ -819,11 +815,10 @@ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+ spin_lock_init(&pClient->lock);
+ pClient->sig_flags = arg;
+ pClient->pid = get_pid(pid);
+- pClient->next = pInfo->firstClient;
+ INIT_LIST_HEAD(&pClient->msgs);
+ pClient->next_block_to_read = NULL;
+ pClient->msg_count = 0;
+- pInfo->firstClient = pClient;
++ list_add(&pClient->node, &pInfo->clients);
+ spin_unlock_irqrestore(&pInfo->lock, flags);
+ }
+ }
+@@ -1001,7 +996,7 @@ static int r3964_open(struct tty_struct *tty)
+ pInfo->tx_position = 0;
+ pInfo->last_rx = 0;
+ pInfo->blocks_in_rx_queue = 0;
+- pInfo->firstClient = NULL;
++ INIT_LIST_HEAD(&pInfo->clients);
+ pInfo->state = R3964_IDLE;
+ pInfo->flags = R3964_DEBUG;
+ pInfo->nRetry = 0;
+@@ -1017,7 +1012,7 @@ static int r3964_open(struct tty_struct *tty)
+ static void r3964_close(struct tty_struct *tty)
+ {
+ struct r3964_info *pInfo = tty->disc_data;
+- struct r3964_client_info *pClient, *pNext;
++ struct r3964_client_info *pClient, *tmp_client;
+ struct r3964_message *pMsg;
+ struct tx_block_header *pHeader, *tmp;
+ unsigned long flags;
+@@ -1031,9 +1026,7 @@ static void r3964_close(struct tty_struct *tty)
+ del_timer_sync(&pInfo->tmr);
+
+ /* Remove client-structs and message queues: */
+- pClient = pInfo->firstClient;
+- while (pClient) {
+- pNext = pClient->next;
++ list_for_each_entry_safe(pClient, tmp_client, &pInfo->clients, node) {
+ while (pClient->msg_count) {
+ pMsg = remove_msg(pClient);
+ if (pMsg) {
+@@ -1042,9 +1035,9 @@ static void r3964_close(struct tty_struct *tty)
+ }
+ }
+ put_pid(pClient->pid);
++ list_del(&pClient->node);
+ kfree(pClient);
+ TRACE_M("r3964_close - client kfree %p", pClient);
+- pClient = pNext;
+ }
+ /* Remove jobs from tx_queue: */
+ spin_lock_irqsave(&pInfo->lock, flags);
+--
+2.21.0
+
diff --git a/0011-tty-n_r3964-fix-race-with-add_msg-and-read_telegram.patch b/0011-tty-n_r3964-fix-race-with-add_msg-and-read_telegram.patch
new file mode 100644
index 00000000000000..8823e994d83ef4
--- /dev/null
+++ b/0011-tty-n_r3964-fix-race-with-add_msg-and-read_telegram.patch
@@ -0,0 +1,113 @@
+From b54481aa9fb9cab7a0ca3257c4a6ba6082ef9652 Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Fri, 25 Jan 2019 15:03:15 +0100
+Subject: [PATCH 11/15] tty: n_r3964: fix race with add_msg() and read_telegram
+
+read_telegram() could race with the client block access code that can be
+called through add_msg() as it did not hold any locks.
+
+Fix that up by properly holding the client->lock when touching the next
+message to be read structures. Gyrations ensue due to the data having
+to be copied to userspace, but the potential corruption problem is now
+gone, to be replaced with a possible race where we free a block that no
+one sent to userspace yet. Given that no one has reported this problem
+in the 20+ years of this code, I'll take the potential race condition
+over a known corruption problems.
+
+Reported-by: Jann Horn <jannh@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 52 ++++++++++++++++++++++++++++++++++---------
+ 1 file changed, 42 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index f752eec92448..1514b17f5615 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -193,8 +193,6 @@ static void receive_char(struct r3964_info *pInfo, const unsigned char c);
+ static void receive_error(struct r3964_info *pInfo, const char flag);
+ static void on_timeout(struct timer_list *t);
+ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg);
+-static int read_telegram(struct r3964_info *pInfo, struct pid *pid,
+- 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_client_info *client);
+@@ -831,6 +829,10 @@ static int read_telegram(struct r3964_info *pInfo, struct pid *pid,
+ {
+ struct r3964_client_info *pClient;
+ struct rx_block_header *block;
++ unsigned long flags;
++ unsigned int length;
++ int retval = 0;
++ u8 *data;
+
+ if (!buf) {
+ return -EINVAL;
+@@ -841,18 +843,47 @@ static int read_telegram(struct r3964_info *pInfo, struct pid *pid,
+ return -EINVAL;
+ }
+
++ spin_lock_irqsave(&pClient->lock, flags);
++
+ block = pClient->next_block_to_read;
+- if (!block) {
+- return 0;
+- } else {
+- if (copy_to_user(buf, block->data, block->length))
+- return -EFAULT;
++ if (!block)
++ goto exit;
++
++ /*
++ * Duplicate the data so we can release the lock while we copy to
++ * userspace
++ */
++ length = block->length;
++ data = kmemdup(block->data, length, GFP_ATOMIC);
++ if (!data) {
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ spin_unlock_irqrestore(&pClient->lock, flags);
+
+- remove_client_block(pClient);
+- return block->length;
++ if (copy_to_user(buf, data, length)) {
++ kfree(data);
++ return -EFAULT;
+ }
++ kfree(data);
++
++ /*
++ * Copy succeeded, so grab the lock again, and then drop the buffer, as
++ * remove_client_block() has to have the lock held.
++ *
++ * Note, the client's next_block_to_read could have changed here, so
++ * worst case, we just dropped a buffer that wasn't read. But, nothing
++ * was corrupted or accidentally freed, so we are doing better than we
++ * used to. Ideally the whole issue of "read_telegram" would be handled
++ * some other way as this races with the add_msg() path in a bad manner
++ */
++ spin_lock_irqsave(&pClient->lock, flags);
++ remove_client_block(pClient);
+
+- return -EINVAL;
++exit:
++ spin_unlock_irqrestore(&pClient->lock, flags);
++ return retval;
+ }
+
+ static void __add_msg(struct r3964_client_info *pClient, int msg_id, int arg,
+@@ -935,6 +966,7 @@ static struct r3964_message *remove_msg(struct r3964_client_info *client)
+ }
+
+ static void remove_client_block(struct r3964_client_info *client)
++ __must_hold(&client->lock)
+ {
+ struct rx_block_header *block;
+
+--
+2.21.0
+
diff --git a/0012-tty-n_r3964-remove-read_lock-from-some-ioctls.patch b/0012-tty-n_r3964-remove-read_lock-from-some-ioctls.patch
new file mode 100644
index 00000000000000..cd450701c65fb6
--- /dev/null
+++ b/0012-tty-n_r3964-remove-read_lock-from-some-ioctls.patch
@@ -0,0 +1,92 @@
+From 3e1d022e31e12398690f4f8638453b3929fa73d4 Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Fri, 25 Jan 2019 15:20:31 +0100
+Subject: [PATCH 12/15] tty: n_r3964: remove read_lock from some ioctls
+
+Now that read_telegram() properly grabs the needed locks for its
+operation, we can move the "heavy" read lock away from all ioctls except
+one, the R3964_ENABLE_SIGNALS lock.
+
+When we move this lock away, properly grab the info->lock for the other
+ioctls that need it so that they will not have any problems.
+
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 28 ++++++++++++++++++----------
+ 1 file changed, 18 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index 1514b17f5615..d3f53fa201b7 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -175,7 +175,8 @@ struct r3964_info {
+ unsigned char bcc;
+ unsigned int blocks_in_rx_queue;
+
+- struct mutex read_lock; /* serialize read and ioctl */
++ /* serialize read and R3964_ENABLE_SIGNALS ioctl */
++ struct mutex read_lock;
+
+ struct list_head clients;
+ unsigned int state;
+@@ -1219,33 +1220,41 @@ static int r3964_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+ {
+ struct r3964_info *pInfo = tty->disc_data;
++ unsigned long flags;
+ int retval = 0;
+
+ if (pInfo == NULL)
+ return -EINVAL;
+- /* Internal serialization of reads and ioctls */
+- if (file->f_flags & O_NONBLOCK) {
+- if (!mutex_trylock(&pInfo->read_lock))
+- return -EAGAIN;
+- } else {
+- if (mutex_lock_interruptible(&pInfo->read_lock))
+- return -ERESTARTSYS;
+- }
+
+ switch (cmd) {
+ case R3964_ENABLE_SIGNALS:
++ /* Internal serialization of reads and this ioctl */
++ if (file->f_flags & O_NONBLOCK) {
++ if (!mutex_trylock(&pInfo->read_lock))
++ return -EAGAIN;
++ } else {
++ if (mutex_lock_interruptible(&pInfo->read_lock))
++ return -ERESTARTSYS;
++ }
++
+ retval = enable_signals(pInfo, task_pid(current), arg);
++
++ mutex_unlock(&pInfo->read_lock);
+ break;
+ case R3964_SETPRIORITY:
+ if (arg < R3964_MASTER || arg > R3964_SLAVE)
+ return -EINVAL;
++ spin_lock_irqsave(&pInfo->lock, flags);
+ pInfo->priority = arg & 0xff;
++ spin_unlock_irqrestore(&pInfo->lock, flags);
+ break;
+ case R3964_USE_BCC:
++ spin_lock_irqsave(&pInfo->lock, flags);
+ if (arg)
+ pInfo->flags |= R3964_BCC;
+ else
+ pInfo->flags &= ~R3964_BCC;
++ spin_unlock_irqrestore(&pInfo->lock, flags);
+ break;
+ case R3964_READ_TELEGRAM:
+ retval = read_telegram(pInfo, task_pid(current),
+@@ -1256,7 +1265,6 @@ static int r3964_ioctl(struct tty_struct *tty, struct file *file,
+ break;
+ }
+
+- mutex_unlock(&pInfo->read_lock);
+ return retval;
+ }
+
+--
+2.21.0
+
diff --git a/0013-tty-n_r3964-properly-protect-sig_flags-of-client-str.patch b/0013-tty-n_r3964-properly-protect-sig_flags-of-client-str.patch
new file mode 100644
index 00000000000000..cb1010fbeb2d2c
--- /dev/null
+++ b/0013-tty-n_r3964-properly-protect-sig_flags-of-client-str.patch
@@ -0,0 +1,82 @@
+From 7105eeab4aa73032fe40f5e72f51601da0cd7c4f Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Fri, 25 Jan 2019 16:35:57 +0100
+Subject: [PATCH 13/15] tty: n_r3964: properly protect sig_flags of client
+ structure
+
+This cleans up the remaining users of the sig_flags field in the client
+structure to allways access it under the client structure's lock.
+
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 20 ++++++++++++++++----
+ 1 file changed, 16 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index d3f53fa201b7..a00c34ea9c27 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -193,7 +193,6 @@ static void transmit_block(struct r3964_info *pInfo);
+ static void receive_char(struct r3964_info *pInfo, const unsigned char c);
+ static void receive_error(struct r3964_info *pInfo, const char flag);
+ static void on_timeout(struct timer_list *t);
+-static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg);
+ 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_client_info *client);
+@@ -548,7 +547,14 @@ static void on_receive_block(struct r3964_info *pInfo)
+ /* notify attached client processes: */
+ spin_lock_irqsave(&pInfo->lock, flags);
+ list_for_each_entry(pClient, &pInfo->clients, node) {
+- if (pClient->sig_flags & R3964_SIG_DATA) {
++ unsigned long client_flags;
++ unsigned int sig_flags;
++
++ spin_lock_irqsave(&pClient->lock, client_flags);
++ sig_flags = pClient->sig_flags;
++ spin_unlock_irqrestore(&pClient->lock, client_flags);
++
++ if (sig_flags & R3964_SIG_DATA) {
+ add_msg(pClient, R3964_MSG_DATA, length, R3964_OK,
+ pBlock);
+ }
+@@ -799,8 +805,12 @@ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+ } else {
+ pClient = findClient(pInfo, pid);
+ if (pClient) {
++ unsigned long client_flags;
++
+ /* update signal options */
++ spin_lock_irqsave(&pClient->lock, client_flags);
+ pClient->sig_flags = arg;
++ spin_unlock_irqrestore(&pClient->lock, client_flags);
+ } else {
+ /* add client to client list */
+ pClient = kmalloc(sizeof(struct r3964_client_info),
+@@ -915,6 +925,7 @@ static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg,
+ {
+ struct r3964_message *pMsg;
+ unsigned long flags;
++ unsigned int sig_flags;
+
+ spin_lock_irqsave(&pClient->lock, flags);
+
+@@ -935,12 +946,13 @@ static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg,
+ TRACE_PE("add_msg - queue OVERFLOW-msg");
+ }
+ }
++
++ sig_flags = pClient->sig_flags;
+ spin_unlock_irqrestore(&pClient->lock, flags);
+
+ /* Send SIGIO signal to client process: */
+- if (pClient->sig_flags & R3964_USE_SIGIO) {
++ if (sig_flags & R3964_USE_SIGIO)
+ kill_pid(pClient->pid, SIGIO, 1);
+- }
+ }
+
+ static struct r3964_message *remove_msg(struct r3964_client_info *client)
+--
+2.21.0
+
diff --git a/0014-tty-n_r3964-properly-reference-count-pids.patch b/0014-tty-n_r3964-properly-reference-count-pids.patch
new file mode 100644
index 00000000000000..68aec18727fa26
--- /dev/null
+++ b/0014-tty-n_r3964-properly-reference-count-pids.patch
@@ -0,0 +1,103 @@
+From 4607af3f8237114c8c679e5d976ef00fe7053123 Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Fri, 25 Jan 2019 16:47:16 +0100
+Subject: [PATCH 14/15] tty: n_r3964: properly reference count pids
+
+The driver likes to look up things based on the current pid, yet the
+structure is never properly reference counted when passing around the
+pointer. Luckily when it is saved off it is correct, but for all other
+usages, we need to handle the reference properly.
+
+The function find_client_current() is created to handle some of the
+common housekeeping when trying to lookup a structure on the current
+pid.
+
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 28 ++++++++++++++++++++++------
+ 1 file changed, 22 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index a00c34ea9c27..aef0befd068d 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -770,6 +770,18 @@ static struct r3964_client_info *findClient(struct r3964_info *pInfo,
+ return pClient;
+ }
+
++/* Find a client that refers to the pid of the current task */
++static struct r3964_client_info *find_client_current(struct r3964_info *info)
++{
++ struct r3964_client_info *client;
++ struct pid *pid;
++
++ pid = get_pid(task_pid(current));
++ client = findClient(info, pid);
++ put_pid(pid);
++ return client;
++}
++
+ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+ {
+ struct r3964_client_info *pClient;
+@@ -1121,7 +1133,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
+ return -ERESTARTSYS;
+ }
+
+- pClient = findClient(pInfo, task_pid(current));
++ pClient = find_client_current(pInfo);
+ if (pClient) {
+ pMsg = remove_msg(pClient);
+ if (pMsg == NULL) {
+@@ -1208,7 +1220,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
+ pHeader->length = count;
+ pHeader->owner = NULL;
+
+- pClient = findClient(pInfo, task_pid(current));
++ pClient = find_client_current(pInfo);
+ if (pClient) {
+ pHeader->owner = pClient;
+ }
+@@ -1232,6 +1244,7 @@ static int r3964_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+ {
+ struct r3964_info *pInfo = tty->disc_data;
++ struct pid *pid;
+ unsigned long flags;
+ int retval = 0;
+
+@@ -1249,7 +1262,9 @@ static int r3964_ioctl(struct tty_struct *tty, struct file *file,
+ return -ERESTARTSYS;
+ }
+
+- retval = enable_signals(pInfo, task_pid(current), arg);
++ pid = get_pid(task_pid(current));
++ retval = enable_signals(pInfo, pid, arg);
++ put_pid(pid);
+
+ mutex_unlock(&pInfo->read_lock);
+ break;
+@@ -1269,8 +1284,9 @@ static int r3964_ioctl(struct tty_struct *tty, struct file *file,
+ spin_unlock_irqrestore(&pInfo->lock, flags);
+ break;
+ case R3964_READ_TELEGRAM:
+- retval = read_telegram(pInfo, task_pid(current),
+- (unsigned char __user *)arg);
++ pid = get_pid(task_pid(current));
++ retval = read_telegram(pInfo, pid, (unsigned char __user *)arg);
++ put_pid(pid);
+ break;
+ default:
+ retval = -ENOIOCTLCMD;
+@@ -1311,7 +1327,7 @@ static __poll_t r3964_poll(struct tty_struct *tty, struct file *file,
+
+ TRACE_L("POLL");
+
+- pClient = findClient(pInfo, task_pid(current));
++ pClient = find_client_current(pInfo);
+ if (pClient) {
+ poll_wait(file, &tty->read_wait, wait);
+ spin_lock_irqsave(&pClient->lock, flags);
+--
+2.21.0
+
diff --git a/0015-tty-n_r3964-add-reference-counting-to-the-client-str.patch b/0015-tty-n_r3964-add-reference-counting-to-the-client-str.patch
new file mode 100644
index 00000000000000..d7fbe7d31aa68e
--- /dev/null
+++ b/0015-tty-n_r3964-add-reference-counting-to-the-client-str.patch
@@ -0,0 +1,250 @@
+From da04c3926a73304489a320796045814ac48f2e37 Mon Sep 17 00:00:00 2001
+From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Date: Fri, 1 Feb 2019 10:44:55 +0100
+Subject: [PATCH 15/15] tty: n_r3964: add reference counting to the client
+ structure
+
+The client structure pointer is thrown around a lot, and trying to keep
+track of who has, and does not have, a valid pointer is almost
+impossible to audit properly, especially when looking up client
+structures from the lists. So add a kref to the structure so that it
+will be automatically reference counted and no one can access a stale
+pointer and memory will be freed when everything is finished.
+
+This should resolve the problem with the client structure pointer beging
+able to be accessed when it was removed by someone else at the same
+time.
+
+Reported-by: Jann Horn <jannh@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_r3964.c | 82 +++++++++++++++++++++++++++++++++----------
+ 1 file changed, 64 insertions(+), 18 deletions(-)
+
+diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
+index aef0befd068d..37d03ef8e75d 100644
+--- a/drivers/tty/n_r3964.c
++++ b/drivers/tty/n_r3964.c
+@@ -106,6 +106,7 @@ struct tx_block_header;
+
+ struct r3964_client_info {
+ spinlock_t lock;
++ struct kref kref;
+ struct pid *pid;
+ unsigned int sig_flags;
+
+@@ -298,6 +299,30 @@ static int __init r3964_init(void)
+ module_init(r3964_init);
+ module_exit(r3964_exit);
+
++static struct r3964_client_info *client_get(struct r3964_client_info *client)
++{
++ if (client)
++ kref_get(&client->kref);
++ return client;
++}
++
++static void client_free(struct kref *kref)
++{
++ struct r3964_client_info *client;
++
++ client = container_of(kref, struct r3964_client_info, kref);
++
++ put_pid(client->pid);
++ list_del(&client->node);
++ kfree(client);
++}
++
++static void client_put(struct r3964_client_info *client)
++{
++ if (client)
++ kref_put(&client->kref, client_free);
++}
++
+ /*************************************************************
+ * Protocol implementation routines
+ *************************************************************/
+@@ -339,6 +364,7 @@ static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code)
+ wake_up_interruptible(&pInfo->tty->read_wait);
+ }
+
++ client_put(pHeader->owner);
+ kfree(pHeader);
+ }
+
+@@ -550,6 +576,7 @@ static void on_receive_block(struct r3964_info *pInfo)
+ unsigned long client_flags;
+ unsigned int sig_flags;
+
++ client_get(pClient);
+ spin_lock_irqsave(&pClient->lock, client_flags);
+ sig_flags = pClient->sig_flags;
+ spin_unlock_irqrestore(&pClient->lock, client_flags);
+@@ -558,6 +585,7 @@ static void on_receive_block(struct r3964_info *pInfo)
+ add_msg(pClient, R3964_MSG_DATA, length, R3964_OK,
+ pBlock);
+ }
++ client_put(pClient);
+ }
+ spin_unlock_irqrestore(&pInfo->lock, flags);
+ wake_up_interruptible(&pInfo->tty->read_wait);
+@@ -752,25 +780,40 @@ static void on_timeout(struct timer_list *t)
+ }
+ }
+
++/*
++ * The reference count of the pointer returned will be incremented
++ * (if it is not NULL) so client_put() must be called on it when the
++ * caller is finished with it.
++ */
+ static struct r3964_client_info *findClient(struct r3964_info *pInfo,
+ struct pid *pid)
+ {
+ struct r3964_client_info *pClient;
+- unsigned long flags;
++ unsigned long client_flags;
++ unsigned long info_flags;
+
+- spin_lock_irqsave(&pInfo->lock, flags);
++ spin_lock_irqsave(&pInfo->lock, info_flags);
+ list_for_each_entry(pClient, &pInfo->clients, node) {
++ client_get(pClient);
++ spin_lock_irqsave(&pClient->lock, client_flags);
+ if (pClient->pid == pid) {
++ spin_unlock_irqrestore(&pClient->lock, client_flags);
+ goto exit;
+ }
++ spin_unlock_irqrestore(&pClient->lock, client_flags);
++ client_put(pClient);
+ }
+ pClient = NULL;
+ exit:
+- spin_unlock_irqrestore(&pInfo->lock, flags);
++ spin_unlock_irqrestore(&pInfo->lock, info_flags);
+ return pClient;
+ }
+
+-/* Find a client that refers to the pid of the current task */
++/*
++ * Find a client that refers to the pid of the current task
++ * The reference count of the pointer will be incremented so
++ * client_put() must be called when the caller is finished with it
++ */
+ static struct r3964_client_info *find_client_current(struct r3964_info *info)
+ {
+ struct r3964_client_info *client;
+@@ -793,6 +836,7 @@ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+
+ /* Remove client from client list */
+ list_for_each_entry(pClient, &pInfo->clients, node) {
++ client_get(pClient);
+ if (pClient->pid == pid) {
+ TRACE_PS("removing client %d from client list",
+ pid_nr(pid));
+@@ -804,13 +848,13 @@ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+ "kfree %p", pMsg);
+ }
+ }
+- put_pid(pClient->pid);
+- list_del(&pClient->node);
+- kfree(pClient);
+- TRACE_M("enable_signals - kfree %p", pClient);
++ /* Second put will free the structure */
++ client_put(pClient);
++ client_put(pClient);
+ spin_unlock_irqrestore(&pInfo->lock, flags);
+ return 0;
+ }
++ client_put(pClient);
+ }
+ spin_unlock_irqrestore(&pInfo->lock, flags);
+ return -EINVAL;
+@@ -823,6 +867,7 @@ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+ spin_lock_irqsave(&pClient->lock, client_flags);
+ pClient->sig_flags = arg;
+ spin_unlock_irqrestore(&pClient->lock, client_flags);
++ client_put(pClient);
+ } else {
+ /* add client to client list */
+ pClient = kmalloc(sizeof(struct r3964_client_info),
+@@ -834,6 +879,7 @@ static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg)
+ spin_lock_irqsave(&pInfo->lock, flags);
+ TRACE_PS("add client %d to client list", pid_nr(pid));
+ spin_lock_init(&pClient->lock);
++ kref_init(&pClient->kref);
+ pClient->sig_flags = arg;
+ pClient->pid = get_pid(pid);
+ INIT_LIST_HEAD(&pClient->msgs);
+@@ -887,6 +933,7 @@ static int read_telegram(struct r3964_info *pInfo, struct pid *pid,
+
+ if (copy_to_user(buf, data, length)) {
+ kfree(data);
++ client_put(pClient);
+ return -EFAULT;
+ }
+ kfree(data);
+@@ -906,6 +953,7 @@ static int read_telegram(struct r3964_info *pInfo, struct pid *pid,
+
+ exit:
+ spin_unlock_irqrestore(&pClient->lock, flags);
++ client_put(pClient);
+ return retval;
+ }
+
+@@ -1091,10 +1139,7 @@ static void r3964_close(struct tty_struct *tty)
+ TRACE_M("r3964_close - msg kfree %p", pMsg);
+ }
+ }
+- put_pid(pClient->pid);
+- list_del(&pClient->node);
+- kfree(pClient);
+- TRACE_M("r3964_close - client kfree %p", pClient);
++ client_put(pClient);
+ }
+ /* Remove jobs from tx_queue: */
+ spin_lock_irqsave(&pInfo->lock, flags);
+@@ -1174,6 +1219,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
+ ret = -EPERM;
+ unlock:
+ mutex_unlock(&pInfo->read_lock);
++ client_put(pClient);
+ return ret;
+ }
+
+@@ -1182,7 +1228,6 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
+ {
+ struct r3964_info *pInfo = tty->disc_data;
+ struct tx_block_header *pHeader;
+- struct r3964_client_info *pClient;
+ unsigned char *new_data;
+
+ TRACE_L("write request, %d characters", count);
+@@ -1218,12 +1263,12 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
+ pHeader = (struct tx_block_header *)new_data;
+ pHeader->data = new_data + sizeof(*pHeader);
+ pHeader->length = count;
+- pHeader->owner = NULL;
+
+- pClient = find_client_current(pInfo);
+- if (pClient) {
+- pHeader->owner = pClient;
+- }
++ /*
++ * The reference count is left incremented as the last user of
++ * this pointer will drop it when needed
++ */
++ pHeader->owner = find_client_current(pInfo);
+
+ memcpy(pHeader->data, data, count); /* We already verified this */
+
+@@ -1334,6 +1379,7 @@ static __poll_t r3964_poll(struct tty_struct *tty, struct file *file,
+ if (!list_empty(&pClient->msgs))
+ result |= EPOLLIN | EPOLLRDNORM;
+ spin_unlock_irqrestore(&pClient->lock, flags);
++ client_put(pClient);
+ } else {
+ result = EPOLLNVAL | EPOLLERR;
+ }
+--
+2.21.0
+
diff --git a/p04 b/p04
index f4bd17fbd45a99..3255210112b8a0 100644
--- a/p04
+++ b/p04
@@ -49,7 +49,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
--- a/arch/powerpc/kernel/fadump.c
+++ b/arch/powerpc/kernel/fadump.c
-@@ -1649,7 +1649,6 @@ DEFINE_SHOW_ATTRIBUTE(fadump_region);
+@@ -1650,7 +1650,6 @@ DEFINE_SHOW_ATTRIBUTE(fadump_region);
static void fadump_init_files(void)
{
@@ -57,7 +57,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
int rc = 0;
rc = sysfs_create_file(kernel_kobj, &fadump_attr.attr);
-@@ -1662,12 +1661,8 @@ static void fadump_init_files(void)
+@@ -1663,12 +1662,8 @@ static void fadump_init_files(void)
printk(KERN_ERR "fadump: unable to create sysfs file"
" fadump_registered (%d)\n", rc);
@@ -74,7 +74,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
rc = sysfs_create_file(kernel_kobj, &fadump_release_attr.attr);
--- a/arch/powerpc/kernel/setup-common.c
+++ b/arch/powerpc/kernel/setup-common.c
-@@ -779,8 +779,7 @@ EXPORT_SYMBOL(powerpc_debugfs_root);
+@@ -771,8 +771,7 @@ EXPORT_SYMBOL(powerpc_debugfs_root);
static int powerpc_debugfs_init(void)
{
powerpc_debugfs_root = debugfs_create_dir("powerpc", NULL);
@@ -86,7 +86,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
#endif
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
-@@ -2257,35 +2257,20 @@ void ppc_warn_emulated_print(const char
+@@ -2265,35 +2265,20 @@ void ppc_warn_emulated_print(const char
static int __init ppc_warn_emulated_init(void)
{
@@ -158,7 +158,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
int kvmppc_radix_init(void)
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
-@@ -2160,14 +2160,9 @@ static void debugfs_vcpu_init(struct kvm
+@@ -2161,14 +2161,9 @@ static void debugfs_vcpu_init(struct kvm
struct kvm *kvm = vcpu->kvm;
snprintf(buf, sizeof(buf), "vcpu%u", id);
@@ -322,7 +322,7 @@ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
/*
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
-@@ -3107,11 +3107,6 @@ static void pnv_pci_ioda_create_dbgfs(vo
+@@ -3108,11 +3108,6 @@ static void pnv_pci_ioda_create_dbgfs(vo
sprintf(name, "PCI%04x", hose->global_number);
phb->dbgfs = debugfs_create_dir(name, powerpc_debugfs_root);
diff --git a/series b/series
index 575b3784f60fd6..54e94da2fb2c53 100644
--- a/series
+++ b/series
@@ -1,5 +1,20 @@
#
+0001-tty-n_r3964-locking-fixups.patch
+0002-tty-n_r3964-fix-poll-return-value.patch
+0003-tty-n_r3964-remove-n_r3964.h.patch
+0004-tty-n_r3964-drop-ancient-header-changelog-text.patch
+0005-tty-n_r3964-split-rx-and-tx-header-structures.patch
+0006-tty-n_r3964-for-tx_blocks-use-a-real-kernel-list.patch
+0007-tty-n_r3964-for-rx_blocks-use-a-real-kernel-list.patch
+0008-tty-n_r3964-don-t-hand-roll-a-reference-count.patch
+0009-tty-n_r3964-for-messages-use-a-real-kernel-list.patch
+0010-tty-n_r3964-for-clients-use-a-real-kernel-list.patch
+0011-tty-n_r3964-fix-race-with-add_msg-and-read_telegram.patch
+0012-tty-n_r3964-remove-read_lock-from-some-ioctls.patch
+0013-tty-n_r3964-properly-protect-sig_flags-of-client-str.patch
+0014-tty-n_r3964-properly-reference-count-pids.patch
+0015-tty-n_r3964-add-reference-counting-to-the-client-str.patch
l.patch
spdxcheck-print-out-files-without-any-spdx-lines.patch
p02