summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian King <brking@linux.vnet.ibm.com>2023-02-03 16:58:02 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2023-02-08 13:09:15 +0100
commit28d49f8cbe9c7966f91ee1b5ec2f997f6e55bf9f (patch)
treec282d5a44f85d52f2a5ff9cc4cea149e9479ecf8
parenthvcs: Use vhangup in hotplug remove (diff)
downloadlinux-28d49f8cbe9c7966f91ee1b5ec2f997f6e55bf9f.tar.xz
linux-28d49f8cbe9c7966f91ee1b5ec2f997f6e55bf9f.zip
hvcs: Synchronize hotplug remove with port free
Synchronizes hotplug remove with the freeing of the port. This ensures we have freed all the memory associated with this port and are not leaking memory. Signed-off-by: Brian King <brking@linux.vnet.ibm.com> Link: https://lore.kernel.org/r/20230203155802.404324-6-brking@linux.vnet.ibm.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/tty/hvc/hvcs.c26
1 files changed, 15 insertions, 11 deletions
diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c
index ecf24195b1e9..1de1a09bf82d 100644
--- a/drivers/tty/hvc/hvcs.c
+++ b/drivers/tty/hvc/hvcs.c
@@ -52,6 +52,7 @@
#include <linux/device.h>
#include <linux/init.h>
+#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/kref.h>
@@ -285,6 +286,7 @@ struct hvcs_struct {
char p_location_code[HVCS_CLC_LENGTH + 1]; /* CLC + Null Term */
struct list_head next; /* list management */
struct vio_dev *vdev;
+ struct completion *destroyed;
};
static LIST_HEAD(hvcs_structs);
@@ -663,11 +665,13 @@ static void hvcs_destruct_port(struct tty_port *p)
{
struct hvcs_struct *hvcsd = container_of(p, struct hvcs_struct, port);
struct vio_dev *vdev;
+ struct completion *comp;
unsigned long flags;
spin_lock(&hvcs_structs_lock);
spin_lock_irqsave(&hvcsd->lock, flags);
+ comp = hvcsd->destroyed;
/* the list_del poisons the pointers */
list_del(&(hvcsd->next));
@@ -687,6 +691,7 @@ static void hvcs_destruct_port(struct tty_port *p)
hvcsd->p_unit_address = 0;
hvcsd->p_partition_ID = 0;
+ hvcsd->destroyed = NULL;
hvcs_return_index(hvcsd->index);
memset(&hvcsd->p_location_code[0], 0x00, HVCS_CLC_LENGTH + 1);
@@ -694,6 +699,8 @@ static void hvcs_destruct_port(struct tty_port *p)
spin_unlock(&hvcs_structs_lock);
kfree(hvcsd);
+ if (comp)
+ complete(comp);
}
static const struct tty_port_operations hvcs_port_ops = {
@@ -792,6 +799,7 @@ static int hvcs_probe(
static void hvcs_remove(struct vio_dev *dev)
{
struct hvcs_struct *hvcsd = dev_get_drvdata(&dev->dev);
+ DECLARE_COMPLETION_ONSTACK(comp);
unsigned long flags;
struct tty_struct *tty;
@@ -799,17 +807,12 @@ static void hvcs_remove(struct vio_dev *dev)
spin_lock_irqsave(&hvcsd->lock, flags);
+ hvcsd->destroyed = &comp;
tty = tty_port_tty_get(&hvcsd->port);
spin_unlock_irqrestore(&hvcsd->lock, flags);
/*
- * Let the last holder of this object cause it to be removed, which
- * would probably be tty_hangup below.
- */
- tty_port_put(&hvcsd->port);
-
- /*
* The tty should always be valid at this time unless a
* simultaneous tty close already cleaned up the hvcs_struct.
*/
@@ -818,6 +821,8 @@ static void hvcs_remove(struct vio_dev *dev)
tty_kref_put(tty);
}
+ tty_port_put(&hvcsd->port);
+ wait_for_completion(&comp);
printk(KERN_INFO "HVCS: vty-server@%X removed from the"
" vio bus.\n", dev->unit_address);
};
@@ -1171,7 +1176,10 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
hvcsd = tty->driver_data;
spin_lock_irqsave(&hvcsd->lock, flags);
- if (--hvcsd->port.count == 0) {
+ if (hvcsd->port.count == 0) {
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ return;
+ } else if (--hvcsd->port.count == 0) {
vio_disable_interrupts(hvcsd->vdev);
@@ -1227,11 +1235,7 @@ static void hvcs_hangup(struct tty_struct * tty)
vio_disable_interrupts(hvcsd->vdev);
hvcsd->todo_mask = 0;
-
- /* I don't think the tty needs the hvcs_struct pointer after a hangup */
- tty->driver_data = NULL;
hvcsd->port.tty = NULL;
-
hvcsd->port.count = 0;
/* This will drop any buffered data on the floor which is OK in a hangup