From 4898e640caf03fdbaf2122d5a33949bf3e4a5b34 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 16 Apr 2013 06:15:50 -0400 Subject: tty: Add timed, writer-prioritized rw semaphore The semantics of a rw semaphore are almost ideally suited for tty line discipline lifetime management; multiple active threads obtain "references" (read locks) while performing i/o to prevent the loss or change of the current line discipline (write lock). Unfortunately, the existing rw_semaphore is ill-suited in other ways; 1) TIOCSETD ioctl (change line discipline) expects to return an error if the line discipline cannot be exclusively locked within 5 secs. Lock wait timeouts are not supported by rwsem. 2) A tty hangup is expected to halt and scrap pending i/o, so exclusive locking must be prioritized. Writer priority is not supported by rwsem. Add ld_semaphore which implements these requirements in a semantically similar way to rw_semaphore. Writer priority is handled by separate wait lists for readers and writers. Pending write waits are priortized before existing read waits and prevent further read locks. Wait timeouts are trivially added, but obviously change the lock semantics as lock attempts can fail (but only due to timeout). This implementation incorporates the write-lock stealing work of Michel Lespinasse . Cc: Michel Lespinasse Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- include/linux/tty_ldisc.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'include/linux/tty_ldisc.h') diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 58390c73df8b..7b24bbd85ea8 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -110,6 +110,52 @@ #include #include + +/* + * the semaphore definition + */ +struct ld_semaphore { + long count; + raw_spinlock_t wait_lock; + unsigned int wait_readers; + struct list_head read_wait; + struct list_head write_wait; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif +}; + +extern void __init_ldsem(struct ld_semaphore *sem, const char *name, + struct lock_class_key *key); + +#define init_ldsem(sem) \ +do { \ + static struct lock_class_key __key; \ + \ + __init_ldsem((sem), #sem, &__key); \ +} while (0) + + +extern int ldsem_down_read(struct ld_semaphore *sem, long timeout); +extern int ldsem_down_read_trylock(struct ld_semaphore *sem); +extern int ldsem_down_write(struct ld_semaphore *sem, long timeout); +extern int ldsem_down_write_trylock(struct ld_semaphore *sem); +extern void ldsem_up_read(struct ld_semaphore *sem); +extern void ldsem_up_write(struct ld_semaphore *sem); + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +extern int ldsem_down_read_nested(struct ld_semaphore *sem, int subclass, + long timeout); +extern int ldsem_down_write_nested(struct ld_semaphore *sem, int subclass, + long timeout); +#else +# define ldsem_down_read_nested(sem, subclass, timeout) \ + ldsem_down_read(sem, timeout) +# define ldsem_down_write_nested(sem, subclass, timeout) \ + ldsem_down_write(sem, timeout) +#endif + + struct tty_ldisc_ops { int magic; char *name; -- cgit v1.2.3 From f6c8dbe6e50c6e5121d7b6644718207daa008221 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 07:28:28 -0400 Subject: n_tty: Encapsulate minimum_to_wake within N_TTY minimum_to_wake is unique to N_TTY processing, and belongs in per-ldisc data. Add the ldisc method, ldisc_ops::fasync(), to notify line disciplines when signal-driven I/O is enabled or disabled. When enabled for N_TTY (by fcntl(F_SETFL, O_ASYNC)), blocking reader/polls will be woken for any readable input. When disabled, blocking reader/polls are not woken until the read buffer is full. Canonical mode (L_ICANON(tty), n_tty_data::icanon) is not affected by the minimum_to_wake setting. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- include/linux/tty_ldisc.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux/tty_ldisc.h') diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 7b24bbd85ea8..a1b048999821 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -100,6 +100,11 @@ * seek to perform this action quickly but should wait until * any pending driver I/O is completed. * + * void (*fasync)(struct tty_struct *, int on) + * + * Notify line discipline when signal-driven I/O is enabled or + * disabled. + * * void (*dcd_change)(struct tty_struct *tty, unsigned int status) * * Tells the discipline that the DCD pin has changed its status. @@ -189,6 +194,7 @@ struct tty_ldisc_ops { char *fp, int count); void (*write_wakeup)(struct tty_struct *); void (*dcd_change)(struct tty_struct *, unsigned int); + void (*fasync)(struct tty_struct *tty, int on); struct module *owner; -- cgit v1.2.3