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 <brk...@linux.vnet.ibm.com>
---
 drivers/tty/hvc/hvcs.c | 26 +++++++++++++++-----------
 1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c
index 2e9e45f06916..c360965e9c1b 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);
@@ -677,11 +679,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));
 
@@ -701,6 +705,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);
 
@@ -708,6 +713,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 = {
@@ -806,6 +813,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;
 
@@ -813,16 +821,11 @@ 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.
@@ -832,6 +835,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);
 };
@@ -1185,7 +1190,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);
 
@@ -1241,11 +1249,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
-- 
2.31.1

Reply via email to