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>
This commit is contained in:
Brian King 2023-02-03 09:58:02 -06:00 committed by Greg Kroah-Hartman
parent d432228bc7
commit 28d49f8cbe

View file

@ -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,16 +807,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.
@ -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