diff options
Diffstat (limited to 'arch/um/drivers/line.c')
| -rw-r--r-- | arch/um/drivers/line.c | 540 | 
1 files changed, 216 insertions, 324 deletions
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index 7f7338c9078..8035145f043 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -3,15 +3,15 @@   * Licensed under the GPL   */ -#include "linux/irqreturn.h" -#include "linux/kd.h" -#include "linux/sched.h" -#include "linux/slab.h" -#include "chan_kern.h" -#include "irq_kern.h" -#include "irq_user.h" -#include "kern_util.h" -#include "os.h" +#include <linux/irqreturn.h> +#include <linux/kd.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include "chan.h" +#include <irq_kern.h> +#include <irq_user.h> +#include <kern_util.h> +#include <os.h>  #define LINE_BUFSIZE 4096 @@ -21,17 +21,9 @@ static irqreturn_t line_interrupt(int irq, void *data)  	struct line *line = chan->line;  	if (line) -		chan_interrupt(&line->chan_list, &line->task, line->tty, irq); -	return IRQ_HANDLED; -} +		chan_interrupt(line, irq); -static void line_timer_cb(struct work_struct *work) -{ -	struct line *line = container_of(work, struct line, task.work); - -	if (!line->throttled) -		chan_interrupt(&line->chan_list, &line->task, line->tty, -			       line->driver->read_irq); +	return IRQ_HANDLED;  }  /* @@ -145,7 +137,7 @@ static int flush_buffer(struct line *line)  		/* line->buffer + LINE_BUFSIZE is the end of the buffer! */  		count = line->buffer + LINE_BUFSIZE - line->head; -		n = write_chan(&line->chan_list, line->head, count, +		n = write_chan(line->chan_out, line->head, count,  			       line->driver->write_irq);  		if (n < 0)  			return n; @@ -162,7 +154,7 @@ static int flush_buffer(struct line *line)  	}  	count = line->tail - line->head; -	n = write_chan(&line->chan_list, line->head, count, +	n = write_chan(line->chan_out, line->head, count,  		       line->driver->write_irq);  	if (n < 0) @@ -176,10 +168,9 @@ void line_flush_buffer(struct tty_struct *tty)  {  	struct line *line = tty->driver_data;  	unsigned long flags; -	int err;  	spin_lock_irqsave(&line->lock, flags); -	err = flush_buffer(line); +	flush_buffer(line);  	spin_unlock_irqrestore(&line->lock, flags);  } @@ -207,7 +198,7 @@ int line_write(struct tty_struct *tty, const unsigned char *buf, int len)  	if (line->head != line->tail)  		ret = buffer_data(line, buf, len);  	else { -		n = write_chan(&line->chan_list, buf, len, +		n = write_chan(line->chan_out, buf, len,  			       line->driver->write_irq);  		if (n < 0) {  			ret = n; @@ -229,97 +220,11 @@ void line_set_termios(struct tty_struct *tty, struct ktermios * old)  	/* nothing */  } -static const struct { -	int  cmd; -	char *level; -	char *name; -} tty_ioctls[] = { -	/* don't print these, they flood the log ... */ -	{ TCGETS,      NULL,       "TCGETS"      }, -	{ TCSETS,      NULL,       "TCSETS"      }, -	{ TCSETSW,     NULL,       "TCSETSW"     }, -	{ TCFLSH,      NULL,       "TCFLSH"      }, -	{ TCSBRK,      NULL,       "TCSBRK"      }, - -	/* general tty stuff */ -	{ TCSETSF,     KERN_DEBUG, "TCSETSF"     }, -	{ TCGETA,      KERN_DEBUG, "TCGETA"      }, -	{ TIOCMGET,    KERN_DEBUG, "TIOCMGET"    }, -	{ TCSBRKP,     KERN_DEBUG, "TCSBRKP"     }, -	{ TIOCMSET,    KERN_DEBUG, "TIOCMSET"    }, - -	/* linux-specific ones */ -	{ TIOCLINUX,   KERN_INFO,  "TIOCLINUX"   }, -	{ KDGKBMODE,   KERN_INFO,  "KDGKBMODE"   }, -	{ KDGKBTYPE,   KERN_INFO,  "KDGKBTYPE"   }, -	{ KDSIGACCEPT, KERN_INFO,  "KDSIGACCEPT" }, -}; - -int line_ioctl(struct tty_struct *tty, struct file * file, -	       unsigned int cmd, unsigned long arg) -{ -	int ret; -	int i; - -	ret = 0; -	switch(cmd) { -#ifdef TIOCGETP -	case TIOCGETP: -	case TIOCSETP: -	case TIOCSETN: -#endif -#ifdef TIOCGETC -	case TIOCGETC: -	case TIOCSETC: -#endif -#ifdef TIOCGLTC -	case TIOCGLTC: -	case TIOCSLTC: -#endif -	/* Note: these are out of date as we now have TCGETS2 etc but this -	   whole lot should probably go away */ -	case TCGETS: -	case TCSETSF: -	case TCSETSW: -	case TCSETS: -	case TCGETA: -	case TCSETAF: -	case TCSETAW: -	case TCSETA: -	case TCXONC: -	case TCFLSH: -	case TIOCOUTQ: -	case TIOCINQ: -	case TIOCGLCKTRMIOS: -	case TIOCSLCKTRMIOS: -	case TIOCPKT: -	case TIOCGSOFTCAR: -	case TIOCSSOFTCAR: -		return -ENOIOCTLCMD; -#if 0 -	case TCwhatever: -		/* do something */ -		break; -#endif -	default: -		for (i = 0; i < ARRAY_SIZE(tty_ioctls); i++) -			if (cmd == tty_ioctls[i].cmd) -				break; -		if (i == ARRAY_SIZE(tty_ioctls)) { -			printk(KERN_ERR "%s: %s: unknown ioctl: 0x%x\n", -			       __func__, tty->name, cmd); -		} -		ret = -ENOIOCTLCMD; -		break; -	} -	return ret; -} -  void line_throttle(struct tty_struct *tty)  {  	struct line *line = tty->driver_data; -	deactivate_chan(&line->chan_list, line->driver->read_irq); +	deactivate_chan(line->chan_in, line->driver->read_irq);  	line->throttled = 1;  } @@ -328,8 +233,7 @@ void line_unthrottle(struct tty_struct *tty)  	struct line *line = tty->driver_data;  	line->throttled = 0; -	chan_interrupt(&line->chan_list, &line->task, tty, -		       line->driver->read_irq); +	chan_interrupt(line, line->driver->read_irq);  	/*  	 * Maybe there is enough stuff pending that calling the interrupt @@ -337,24 +241,24 @@ void line_unthrottle(struct tty_struct *tty)  	 * again and we shouldn't turn the interrupt back on.  	 */  	if (!line->throttled) -		reactivate_chan(&line->chan_list, line->driver->read_irq); +		reactivate_chan(line->chan_in, line->driver->read_irq);  }  static irqreturn_t line_write_interrupt(int irq, void *data)  {  	struct chan *chan = data;  	struct line *line = chan->line; -	struct tty_struct *tty = line->tty;  	int err;  	/* -	 * Interrupts are disabled here because we registered the interrupt with -	 * IRQF_DISABLED (see line_setup_irq). +	 * Interrupts are disabled here because genirq keep irqs disabled when +	 * calling the action handler.  	 */  	spin_lock(&line->lock);  	err = flush_buffer(line);  	if (err == 0) { +		spin_unlock(&line->lock);  		return IRQ_NONE;  	} else if (err < 0) {  		line->head = line->buffer; @@ -362,125 +266,100 @@ static irqreturn_t line_write_interrupt(int irq, void *data)  	}  	spin_unlock(&line->lock); -	if (tty == NULL) -		return IRQ_NONE; +	tty_port_tty_wakeup(&line->port); -	tty_wakeup(tty);  	return IRQ_HANDLED;  }  int line_setup_irq(int fd, int input, int output, struct line *line, void *data)  {  	const struct line_driver *driver = line->driver; -	int err = 0, flags = IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM; +	int err = 0;  	if (input)  		err = um_request_irq(driver->read_irq, fd, IRQ_READ, -				       line_interrupt, flags, -				       driver->read_irq_name, data); +				     line_interrupt, IRQF_SHARED, +				     driver->read_irq_name, data);  	if (err)  		return err;  	if (output)  		err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, -					line_write_interrupt, flags, -					driver->write_irq_name, data); -	line->have_irq = 1; +				     line_write_interrupt, IRQF_SHARED, +				     driver->write_irq_name, data);  	return err;  } -/* - * Normally, a driver like this can rely mostly on the tty layer - * locking, particularly when it comes to the driver structure. - * However, in this case, mconsole requests can come in "from the - * side", and race with opens and closes. - * - * mconsole config requests will want to be sure the device isn't in - * use, and get_config, open, and close will want a stable - * configuration.  The checking and modification of the configuration - * is done under a spinlock.  Checking whether the device is in use is - * line->tty->count > 1, also under the spinlock. - * - * tty->count serves to decide whether the device should be enabled or - * disabled on the host.  If it's equal to 1, then we are doing the - * first open or last close.  Otherwise, open and close just return. - */ - -int line_open(struct line *lines, struct tty_struct *tty) +static int line_activate(struct tty_port *port, struct tty_struct *tty)  { -	struct line *line = &lines[tty->index]; -	int err = -ENODEV; - -	spin_lock(&line->count_lock); -	if (!line->valid) -		goto out_unlock; - -	err = 0; -	if (tty->count > 1) -		goto out_unlock; - -	spin_unlock(&line->count_lock); - -	tty->driver_data = line; -	line->tty = tty; - -	err = enable_chan(line); -	if (err) -		return err; +	int ret; +	struct line *line = tty->driver_data; -	INIT_DELAYED_WORK(&line->task, line_timer_cb); +	ret = enable_chan(line); +	if (ret) +		return ret;  	if (!line->sigio) { -		chan_enable_winch(&line->chan_list, tty); +		chan_enable_winch(line->chan_out, port);  		line->sigio = 1;  	} -	chan_window_size(&line->chan_list, &tty->winsize.ws_row, -			 &tty->winsize.ws_col); - -	return err; +	chan_window_size(line, &tty->winsize.ws_row, +		&tty->winsize.ws_col); -out_unlock: -	spin_unlock(&line->count_lock); -	return err; +	return 0;  }  static void unregister_winch(struct tty_struct *tty); -void line_close(struct tty_struct *tty, struct file * filp) +static void line_destruct(struct tty_port *port)  { +	struct tty_struct *tty = tty_port_tty_get(port);  	struct line *line = tty->driver_data; -	/* -	 * If line_open fails (and tty->driver_data is never set), -	 * tty_open will call line_close.  So just return in this case. -	 */ -	if (line == NULL) -		return; +	if (line->sigio) { +		unregister_winch(tty); +		line->sigio = 0; +	} +} -	/* We ignore the error anyway! */ -	flush_buffer(line); +static const struct tty_port_operations line_port_ops = { +	.activate = line_activate, +	.destruct = line_destruct, +}; -	spin_lock(&line->count_lock); -	if (!line->valid) -		goto out_unlock; +int line_open(struct tty_struct *tty, struct file *filp) +{ +	struct line *line = tty->driver_data; + +	return tty_port_open(&line->port, tty, filp); +} -	if (tty->count > 1) -		goto out_unlock; +int line_install(struct tty_driver *driver, struct tty_struct *tty, +		 struct line *line) +{ +	int ret; -	spin_unlock(&line->count_lock); +	ret = tty_standard_install(driver, tty); +	if (ret) +		return ret; -	line->tty = NULL; -	tty->driver_data = NULL; +	tty->driver_data = line; -	if (line->sigio) { -		unregister_winch(tty); -		line->sigio = 0; -	} +	return 0; +} -	return; +void line_close(struct tty_struct *tty, struct file * filp) +{ +	struct line *line = tty->driver_data; + +	tty_port_close(&line->port, tty, filp); +} + +void line_hangup(struct tty_struct *tty) +{ +	struct line *line = tty->driver_data; -out_unlock: -	spin_unlock(&line->count_lock); +	tty_port_hangup(&line->port);  }  void close_lines(struct line *lines, int nlines) @@ -488,34 +367,58 @@ void close_lines(struct line *lines, int nlines)  	int i;  	for(i = 0; i < nlines; i++) -		close_chan(&lines[i].chan_list, 0); +		close_chan(&lines[i]);  } -static int setup_one_line(struct line *lines, int n, char *init, int init_prio, -			  char **error_out) +int setup_one_line(struct line *lines, int n, char *init, +		   const struct chan_opts *opts, char **error_out)  {  	struct line *line = &lines[n]; +	struct tty_driver *driver = line->driver->driver;  	int err = -EINVAL; -	spin_lock(&line->count_lock); - -	if (line->tty != NULL) { +	if (line->port.count) {  		*error_out = "Device is already open";  		goto out;  	} -	if (line->init_pri <= init_prio) { -		line->init_pri = init_prio; -		if (!strcmp(init, "none")) +	if (!strcmp(init, "none")) { +		if (line->valid) {  			line->valid = 0; -		else { -			line->init_str = init; -			line->valid = 1; +			kfree(line->init_str); +			tty_unregister_device(driver, n); +			parse_chan_pair(NULL, line, n, opts, error_out); +			err = 0; +		} +	} else { +		char *new = kstrdup(init, GFP_KERNEL); +		if (!new) { +			*error_out = "Failed to allocate memory"; +			return -ENOMEM; +		} +		if (line->valid) { +			tty_unregister_device(driver, n); +			kfree(line->init_str); +		} +		line->init_str = new; +		line->valid = 1; +		err = parse_chan_pair(new, line, n, opts, error_out); +		if (!err) { +			struct device *d = tty_port_register_device(&line->port, +					driver, n, NULL); +			if (IS_ERR(d)) { +				*error_out = "Failed to register device"; +				err = PTR_ERR(d); +				parse_chan_pair(NULL, line, n, opts, error_out); +			} +		} +		if (err) { +			line->init_str = NULL; +			line->valid = 0; +			kfree(new);  		}  	} -	err = 0;  out: -	spin_unlock(&line->count_lock);  	return err;  } @@ -526,54 +429,43 @@ out:   * @error_out is an error string in the case of failure;   */ -int line_setup(struct line *lines, unsigned int num, char *init, -	       char **error_out) +int line_setup(char **conf, unsigned int num, char **def, +	       char *init, char *name)  { -	int i, n, err; -	char *end; +	char *error;  	if (*init == '=') {  		/*  		 * We said con=/ssl= instead of con#=, so we are configuring all  		 * consoles at once.  		 */ -		n = -1; -	} -	else { -		n = simple_strtoul(init, &end, 0); +		*def = init + 1; +	} else { +		char *end; +		unsigned n = simple_strtoul(init, &end, 0); +  		if (*end != '=') { -			*error_out = "Couldn't parse device number"; -			return -EINVAL; +			error = "Couldn't parse device number"; +			goto out;  		} -		init = end; -	} -	init++; - -	if (n >= (signed int) num) { -		*error_out = "Device number out of range"; -		return -EINVAL; -	} -	else if (n >= 0) { -		err = setup_one_line(lines, n, init, INIT_ONE, error_out); -		if (err) -			return err; -	} -	else { -		for(i = 0; i < num; i++) { -			err = setup_one_line(lines, i, init, INIT_ALL, -					     error_out); -			if (err) -				return err; +		if (n >= num) { +			error = "Device number out of range"; +			goto out;  		} +		conf[n] = end + 1;  	} -	return n == -1 ? num : n; +	return 0; + +out: +	printk(KERN_ERR "Failed to set up %s with " +	       "configuration string \"%s\" : %s\n", name, init, error); +	return -EINVAL;  }  int line_config(struct line *lines, unsigned int num, char *str,  		const struct chan_opts *opts, char **error_out)  { -	struct line *line; -	char *new; +	char *end;  	int n;  	if (*str == '=') { @@ -581,17 +473,17 @@ int line_config(struct line *lines, unsigned int num, char *str,  		return -EINVAL;  	} -	new = kstrdup(str, GFP_KERNEL); -	if (new == NULL) { -		*error_out = "Failed to allocate memory"; -		return -ENOMEM; +	n = simple_strtoul(str, &end, 0); +	if (*end++ != '=') { +		*error_out = "Couldn't parse device number"; +		return -EINVAL; +	} +	if (n >= num) { +		*error_out = "Device number out of range"; +		return -EINVAL;  	} -	n = line_setup(lines, num, new, error_out); -	if (n < 0) -		return n; -	line = &lines[n]; -	return parse_chan_pair(line->init_str, line, n, opts, error_out); +	return setup_one_line(lines, n, end, opts, error_out);  }  int line_get_config(char *name, struct line *lines, unsigned int num, char *str, @@ -614,13 +506,17 @@ int line_get_config(char *name, struct line *lines, unsigned int num, char *str,  	line = &lines[dev]; -	spin_lock(&line->count_lock);  	if (!line->valid)  		CONFIG_CHUNK(str, size, n, "none", 1); -	else if (line->tty == NULL) -		CONFIG_CHUNK(str, size, n, line->init_str, 1); -	else n = chan_config_string(&line->chan_list, str, size, error_out); -	spin_unlock(&line->count_lock); +	else { +		struct tty_struct *tty = tty_port_tty_get(&line->port); +		if (tty == NULL) { +			CONFIG_CHUNK(str, size, n, line->init_str, 1); +		} else { +			n = chan_config_string(line, str, size, error_out); +			tty_kref_put(tty); +		} +	}  	return n;  } @@ -642,25 +538,23 @@ int line_id(char **str, int *start_out, int *end_out)  int line_remove(struct line *lines, unsigned int num, int n, char **error_out)  { -	int err; -	char config[sizeof("conxxxx=none\0")]; - -	sprintf(config, "%d=none", n); -	err = line_setup(lines, num, config, error_out); -	if (err >= 0) -		err = 0; -	return err; +	if (n >= num) { +		*error_out = "Device number out of range"; +		return -EINVAL; +	} +	return setup_one_line(lines, n, "none", NULL, error_out);  } -struct tty_driver *register_lines(struct line_driver *line_driver, -				  const struct tty_operations *ops, -				  struct line *lines, int nlines) +int register_lines(struct line_driver *line_driver, +		   const struct tty_operations *ops, +		   struct line *lines, int nlines)  { -	int i;  	struct tty_driver *driver = alloc_tty_driver(nlines); +	int err; +	int i;  	if (!driver) -		return NULL; +		return -ENOMEM;  	driver->driver_name = line_driver->name;  	driver->name = line_driver->device_name; @@ -668,108 +562,104 @@ struct tty_driver *register_lines(struct line_driver *line_driver,  	driver->minor_start = line_driver->minor_start;  	driver->type = line_driver->type;  	driver->subtype = line_driver->subtype; -	driver->flags = TTY_DRIVER_REAL_RAW; +	driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;  	driver->init_termios = tty_std_termios; +	 +	for (i = 0; i < nlines; i++) { +		tty_port_init(&lines[i].port); +		lines[i].port.ops = &line_port_ops; +		spin_lock_init(&lines[i].lock); +		lines[i].driver = line_driver; +		INIT_LIST_HEAD(&lines[i].chan_list); +	}  	tty_set_operations(driver, ops); -	if (tty_register_driver(driver)) { +	err = tty_register_driver(driver); +	if (err) {  		printk(KERN_ERR "register_lines : can't register %s driver\n",  		       line_driver->name);  		put_tty_driver(driver); -		return NULL; -	} - -	for(i = 0; i < nlines; i++) { -		if (!lines[i].valid) -			tty_unregister_device(driver, i); +		for (i = 0; i < nlines; i++) +			tty_port_destroy(&lines[i].port); +		return err;  	} +	line_driver->driver = driver;  	mconsole_register_dev(&line_driver->mc); -	return driver; +	return 0;  }  static DEFINE_SPINLOCK(winch_handler_lock);  static LIST_HEAD(winch_handlers); -void lines_init(struct line *lines, int nlines, struct chan_opts *opts) -{ -	struct line *line; -	char *error; -	int i; - -	for(i = 0; i < nlines; i++) { -		line = &lines[i]; -		INIT_LIST_HEAD(&line->chan_list); - -		if (line->init_str == NULL) -			continue; - -		line->init_str = kstrdup(line->init_str, GFP_KERNEL); -		if (line->init_str == NULL) -			printk(KERN_ERR "lines_init - kstrdup returned NULL\n"); - -		if (parse_chan_pair(line->init_str, line, i, opts, &error)) { -			printk(KERN_ERR "parse_chan_pair failed for " -			       "device %d : %s\n", i, error); -			line->valid = 0; -		} -	} -} -  struct winch {  	struct list_head list;  	int fd;  	int tty_fd;  	int pid; -	struct tty_struct *tty; +	struct tty_port *port;  	unsigned long stack; +	struct work_struct work;  }; -static void free_winch(struct winch *winch, int free_irq_ok) +static void __free_winch(struct work_struct *work)  { -	list_del(&winch->list); +	struct winch *winch = container_of(work, struct winch, work); +	um_free_irq(WINCH_IRQ, winch);  	if (winch->pid != -1)  		os_kill_process(winch->pid, 1); -	if (winch->fd != -1) -		os_close_file(winch->fd);  	if (winch->stack != 0)  		free_stack(winch->stack, 0); -	if (free_irq_ok) -		free_irq(WINCH_IRQ, winch);  	kfree(winch);  } +static void free_winch(struct winch *winch) +{ +	int fd = winch->fd; +	winch->fd = -1; +	if (fd != -1) +		os_close_file(fd); +	list_del(&winch->list); +	__free_winch(&winch->work); +} +  static irqreturn_t winch_interrupt(int irq, void *data)  {  	struct winch *winch = data;  	struct tty_struct *tty;  	struct line *line; +	int fd = winch->fd;  	int err;  	char c; -	if (winch->fd != -1) { -		err = generic_read(winch->fd, &c, NULL); +	if (fd != -1) { +		err = generic_read(fd, &c, NULL);  		if (err < 0) {  			if (err != -EAGAIN) { +				winch->fd = -1; +				list_del(&winch->list); +				os_close_file(fd);  				printk(KERN_ERR "winch_interrupt : "  				       "read failed, errno = %d\n", -err);  				printk(KERN_ERR "fd %d is losing SIGWINCH "  				       "support\n", winch->tty_fd); -				free_winch(winch, 0); +				INIT_WORK(&winch->work, __free_winch); +				schedule_work(&winch->work);  				return IRQ_HANDLED;  			}  			goto out;  		}  	} -	tty = winch->tty; +	tty = tty_port_tty_get(winch->port);  	if (tty != NULL) {  		line = tty->driver_data;  		if (line != NULL) { -			chan_window_size(&line->chan_list, &tty->winsize.ws_row, +			chan_window_size(line, &tty->winsize.ws_row,  					 &tty->winsize.ws_col);  			kill_pgrp(tty->pgrp, SIGWINCH, 1);  		} +		tty_kref_put(tty);  	}   out:  	if (winch->fd != -1) @@ -777,7 +667,7 @@ static irqreturn_t winch_interrupt(int irq, void *data)  	return IRQ_HANDLED;  } -void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty, +void register_winch_irq(int fd, int tty_fd, int pid, struct tty_port *port,  			unsigned long stack)  {  	struct winch *winch; @@ -792,12 +682,11 @@ void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty,  				   .fd  	= fd,  				   .tty_fd 	= tty_fd,  				   .pid  	= pid, -				   .tty 	= tty, +				   .port 	= port,  				   .stack	= stack });  	if (um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, -			   IRQF_DISABLED | IRQF_SHARED | IRQF_SAMPLE_RANDOM, -			   "winch", winch) < 0) { +			   IRQF_SHARED, "winch", winch) < 0) {  		printk(KERN_ERR "register_winch_irq - failed to register "  		       "IRQ\n");  		goto out_free; @@ -820,17 +709,20 @@ void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty,  static void unregister_winch(struct tty_struct *tty)  { -	struct list_head *ele; +	struct list_head *ele, *next;  	struct winch *winch; +	struct tty_struct *wtty;  	spin_lock(&winch_handler_lock); -	list_for_each(ele, &winch_handlers) { +	list_for_each_safe(ele, next, &winch_handlers) {  		winch = list_entry(ele, struct winch, list); -		if (winch->tty == tty) { -			free_winch(winch, 1); +		wtty = tty_port_tty_get(winch->port); +		if (wtty == tty) { +			free_winch(winch);  			break;  		} +		tty_kref_put(wtty);  	}  	spin_unlock(&winch_handler_lock);  } @@ -844,7 +736,7 @@ static void winch_cleanup(void)  	list_for_each_safe(ele, next, &winch_handlers) {  		winch = list_entry(ele, struct winch, list); -		free_winch(winch, 1); +		free_winch(winch);  	}  	spin_unlock(&winch_handler_lock);  | 
