diff options
Diffstat (limited to 'drivers/tty/hvc/hvcs.c')
| -rw-r--r-- | drivers/tty/hvc/hvcs.c | 91 | 
1 files changed, 34 insertions, 57 deletions
diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 4ba24963685e..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); @@ -432,7 +434,7 @@ static ssize_t hvcs_index_show(struct device *dev, struct device_attribute *attr  static DEVICE_ATTR(index, S_IRUGO, hvcs_index_show, NULL); -static struct attribute *hvcs_attrs[] = { +static struct attribute *hvcs_dev_attrs[] = {  	&dev_attr_partner_vtys.attr,  	&dev_attr_partner_clcs.attr,  	&dev_attr_current_vty.attr, @@ -441,9 +443,7 @@ static struct attribute *hvcs_attrs[] = {  	NULL,  }; -static struct attribute_group hvcs_attr_group = { -	.attrs = hvcs_attrs, -}; +ATTRIBUTE_GROUPS(hvcs_dev);  static ssize_t rescan_show(struct device_driver *ddp, char *buf)  { @@ -468,6 +468,13 @@ static ssize_t rescan_store(struct device_driver *ddp, const char * buf,  static DRIVER_ATTR_RW(rescan); +static struct attribute *hvcs_attrs[] = { +	&driver_attr_rescan.attr, +	NULL, +}; + +ATTRIBUTE_GROUPS(hvcs); +  static void hvcs_kick(void)  {  	hvcs_kicked = 1; @@ -658,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)); @@ -682,15 +691,16 @@ 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);  	spin_unlock_irqrestore(&hvcsd->lock, flags);  	spin_unlock(&hvcs_structs_lock); -	sysfs_remove_group(&vdev->dev.kobj, &hvcs_attr_group); -  	kfree(hvcsd); +	if (comp) +		complete(comp);  }  static const struct tty_port_operations hvcs_port_ops = { @@ -721,7 +731,6 @@ static int hvcs_probe(  {  	struct hvcs_struct *hvcsd;  	int index, rc; -	int retval;  	if (!dev || !id) {  		printk(KERN_ERR "HVCS: probed with invalid parameter.\n"); @@ -778,13 +787,6 @@ static int hvcs_probe(  	list_add_tail(&(hvcsd->next), &hvcs_structs);  	spin_unlock(&hvcs_structs_lock); -	retval = sysfs_create_group(&dev->dev.kobj, &hvcs_attr_group); -	if (retval) { -		printk(KERN_ERR "HVCS: Can't create sysfs attrs for vty-server@%X\n", -		       hvcsd->vdev->unit_address); -		return retval; -	} -  	printk(KERN_INFO "HVCS: vty-server@%X added to the vio bus.\n", dev->unit_address);  	/* @@ -797,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; @@ -804,24 +807,22 @@ static void hvcs_remove(struct vio_dev *dev)  	spin_lock_irqsave(&hvcsd->lock, flags); -	tty = hvcsd->port.tty; +	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); - -	/* -	 * The hangup is a scheduled function which will auto chain call -	 * hvcs_hangup.  The tty should always be valid at this time unless a +	 * The tty should always be valid at this time unless a  	 * simultaneous tty close already cleaned up the hvcs_struct.  	 */ -	if (tty) -		tty_hangup(tty); +	if (tty) { +		tty_vhangup(tty); +		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);  }; @@ -831,6 +832,10 @@ static struct vio_driver hvcs_vio_driver = {  	.probe		= hvcs_probe,  	.remove		= hvcs_remove,  	.name		= hvcs_driver_name, +	.driver = { +		.groups = hvcs_groups, +		.dev_groups = hvcs_dev_groups, +	},  };  /* Only called from hvcs_get_pi please */ @@ -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); @@ -1215,12 +1223,9 @@ static void hvcs_hangup(struct tty_struct * tty)  {  	struct hvcs_struct *hvcsd = tty->driver_data;  	unsigned long flags; -	int temp_open_count;  	int irq;  	spin_lock_irqsave(&hvcsd->lock, flags); -	/* Preserve this so that we know how many kref refs to put */ -	temp_open_count = hvcsd->port.count;  	/*  	 * Don't kref put inside the spinlock because the destruction @@ -1230,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 @@ -1247,21 +1248,6 @@ static void hvcs_hangup(struct tty_struct * tty)  	spin_unlock_irqrestore(&hvcsd->lock, flags);  	free_irq(irq, hvcsd); - -	/* -	 * We need to kref_put() for every open_count we have since the -	 * tty_hangup() function doesn't invoke a close per open connection on a -	 * non-console device. -	 */ -	while(temp_open_count) { -		--temp_open_count; -		/* -		 * The final put will trigger destruction of the hvcs_struct. -		 * NOTE:  If this hangup was signaled from user space then the -		 * final put will never happen. -		 */ -		tty_port_put(&hvcsd->port); -	}  }  /* @@ -1525,13 +1511,6 @@ static int __init hvcs_module_init(void)  	pr_info("HVCS: Driver registered.\n"); -	/* This needs to be done AFTER the vio_register_driver() call or else -	 * the kobjects won't be initialized properly. -	 */ -	rc = driver_create_file(&(hvcs_vio_driver.driver), &driver_attr_rescan); -	if (rc) -		pr_warn("HVCS: Failed to create rescan file (err %d)\n", rc); -  	return 0;  } @@ -1556,8 +1535,6 @@ static void __exit hvcs_module_exit(void)  	hvcs_pi_buff = NULL;  	spin_unlock(&hvcs_pi_lock); -	driver_remove_file(&hvcs_vio_driver.driver, &driver_attr_rescan); -  	tty_unregister_driver(hvcs_tty_driver);  	hvcs_free_index_list();  |