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@xxxxxxxxxxxxxxxxxx> --- 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 24541fc53625..2b038d4b3a63 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); @@ -658,11 +660,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)); @@ -682,6 +686,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); @@ -689,6 +694,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 = { @@ -795,6 +802,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; @@ -802,16 +810,11 @@ static void hvcs_remove(struct vio_dev *dev) spin_lock_irqsave(&hvcsd->lock, flags); + hvcsd->destroyed = ∁ 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); - sysfs_remove_group(&dev->dev.kobj, &hvcs_attr_group); /* @@ -823,6 +826,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); }; @@ -1172,7 +1177,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); @@ -1228,11 +1236,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