LCOV - code coverage report
Current view: top level - drivers/tty - tty_ldisc.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 129 237 54.4 %
Date: 2014-04-07 Functions: 22 29 75.9 %
Branches: 59 208 28.4 %

           Branch data     Line data    Source code
       1                 :            : #include <linux/types.h>
       2                 :            : #include <linux/errno.h>
       3                 :            : #include <linux/kmod.h>
       4                 :            : #include <linux/sched.h>
       5                 :            : #include <linux/interrupt.h>
       6                 :            : #include <linux/tty.h>
       7                 :            : #include <linux/tty_driver.h>
       8                 :            : #include <linux/file.h>
       9                 :            : #include <linux/mm.h>
      10                 :            : #include <linux/string.h>
      11                 :            : #include <linux/slab.h>
      12                 :            : #include <linux/poll.h>
      13                 :            : #include <linux/proc_fs.h>
      14                 :            : #include <linux/init.h>
      15                 :            : #include <linux/module.h>
      16                 :            : #include <linux/device.h>
      17                 :            : #include <linux/wait.h>
      18                 :            : #include <linux/bitops.h>
      19                 :            : #include <linux/seq_file.h>
      20                 :            : #include <linux/uaccess.h>
      21                 :            : #include <linux/ratelimit.h>
      22                 :            : 
      23                 :            : #undef LDISC_DEBUG_HANGUP
      24                 :            : 
      25                 :            : #ifdef LDISC_DEBUG_HANGUP
      26                 :            : #define tty_ldisc_debug(tty, f, args...) ({                                    \
      27                 :            :         char __b[64];                                                          \
      28                 :            :         printk(KERN_DEBUG "%s: %s: " f, __func__, tty_name(tty, __b), ##args); \
      29                 :            : })
      30                 :            : #else
      31                 :            : #define tty_ldisc_debug(tty, f, args...)
      32                 :            : #endif
      33                 :            : 
      34                 :            : /* lockdep nested classes for tty->ldisc_sem */
      35                 :            : enum {
      36                 :            :         LDISC_SEM_NORMAL,
      37                 :            :         LDISC_SEM_OTHER,
      38                 :            : };
      39                 :            : 
      40                 :            : 
      41                 :            : /*
      42                 :            :  *      This guards the refcounted line discipline lists. The lock
      43                 :            :  *      must be taken with irqs off because there are hangup path
      44                 :            :  *      callers who will do ldisc lookups and cannot sleep.
      45                 :            :  */
      46                 :            : 
      47                 :            : static DEFINE_RAW_SPINLOCK(tty_ldiscs_lock);
      48                 :            : /* Line disc dispatch table */
      49                 :            : static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
      50                 :            : 
      51                 :            : /**
      52                 :            :  *      tty_register_ldisc      -       install a line discipline
      53                 :            :  *      @disc: ldisc number
      54                 :            :  *      @new_ldisc: pointer to the ldisc object
      55                 :            :  *
      56                 :            :  *      Installs a new line discipline into the kernel. The discipline
      57                 :            :  *      is set up as unreferenced and then made available to the kernel
      58                 :            :  *      from this point onwards.
      59                 :            :  *
      60                 :            :  *      Locking:
      61                 :            :  *              takes tty_ldiscs_lock to guard against ldisc races
      62                 :            :  */
      63                 :            : 
      64                 :          0 : int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
      65                 :            : {
      66                 :            :         unsigned long flags;
      67                 :            :         int ret = 0;
      68                 :            : 
      69         [ #  # ]:          0 :         if (disc < N_TTY || disc >= NR_LDISCS)
      70                 :            :                 return -EINVAL;
      71                 :            : 
      72                 :          0 :         raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
      73                 :          0 :         tty_ldiscs[disc] = new_ldisc;
      74                 :          0 :         new_ldisc->num = disc;
      75                 :          0 :         new_ldisc->refcount = 0;
      76                 :          0 :         raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
      77                 :            : 
      78                 :          0 :         return ret;
      79                 :            : }
      80                 :            : EXPORT_SYMBOL(tty_register_ldisc);
      81                 :            : 
      82                 :            : /**
      83                 :            :  *      tty_unregister_ldisc    -       unload a line discipline
      84                 :            :  *      @disc: ldisc number
      85                 :            :  *      @new_ldisc: pointer to the ldisc object
      86                 :            :  *
      87                 :            :  *      Remove a line discipline from the kernel providing it is not
      88                 :            :  *      currently in use.
      89                 :            :  *
      90                 :            :  *      Locking:
      91                 :            :  *              takes tty_ldiscs_lock to guard against ldisc races
      92                 :            :  */
      93                 :            : 
      94                 :          0 : int tty_unregister_ldisc(int disc)
      95                 :            : {
      96                 :            :         unsigned long flags;
      97                 :            :         int ret = 0;
      98                 :            : 
      99         [ #  # ]:          0 :         if (disc < N_TTY || disc >= NR_LDISCS)
     100                 :            :                 return -EINVAL;
     101                 :            : 
     102                 :          0 :         raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
     103         [ #  # ]:          0 :         if (tty_ldiscs[disc]->refcount)
     104                 :            :                 ret = -EBUSY;
     105                 :            :         else
     106                 :          0 :                 tty_ldiscs[disc] = NULL;
     107                 :          0 :         raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
     108                 :            : 
     109                 :          0 :         return ret;
     110                 :            : }
     111                 :            : EXPORT_SYMBOL(tty_unregister_ldisc);
     112                 :            : 
     113                 :          0 : static struct tty_ldisc_ops *get_ldops(int disc)
     114                 :            : {
     115                 :            :         unsigned long flags;
     116                 :            :         struct tty_ldisc_ops *ldops, *ret;
     117                 :            : 
     118                 :        609 :         raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
     119                 :            :         ret = ERR_PTR(-EINVAL);
     120                 :        609 :         ldops = tty_ldiscs[disc];
     121         [ +  + ]:        609 :         if (ldops) {
     122                 :            :                 ret = ERR_PTR(-EAGAIN);
     123         [ +  - ]:        581 :                 if (try_module_get(ldops->owner)) {
     124                 :        581 :                         ldops->refcount++;
     125                 :            :                         ret = ldops;
     126                 :            :                 }
     127                 :            :         }
     128                 :        609 :         raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
     129                 :        609 :         return ret;
     130                 :            : }
     131                 :            : 
     132                 :        577 : static void put_ldops(struct tty_ldisc_ops *ldops)
     133                 :            : {
     134                 :            :         unsigned long flags;
     135                 :            : 
     136                 :        577 :         raw_spin_lock_irqsave(&tty_ldiscs_lock, flags);
     137                 :        577 :         ldops->refcount--;
     138                 :        577 :         module_put(ldops->owner);
     139                 :        577 :         raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
     140                 :        577 : }
     141                 :            : 
     142                 :            : /**
     143                 :            :  *      tty_ldisc_get           -       take a reference to an ldisc
     144                 :            :  *      @disc: ldisc number
     145                 :            :  *
     146                 :            :  *      Takes a reference to a line discipline. Deals with refcounts and
     147                 :            :  *      module locking counts. Returns NULL if the discipline is not available.
     148                 :            :  *      Returns a pointer to the discipline and bumps the ref count if it is
     149                 :            :  *      available
     150                 :            :  *
     151                 :            :  *      Locking:
     152                 :            :  *              takes tty_ldiscs_lock to guard against ldisc races
     153                 :            :  */
     154                 :            : 
     155                 :          0 : static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)
     156                 :            : {
     157                 :            :         struct tty_ldisc *ld;
     158                 :            :         struct tty_ldisc_ops *ldops;
     159                 :            : 
     160         [ +  - ]:        579 :         if (disc < N_TTY || disc >= NR_LDISCS)
     161                 :            :                 return ERR_PTR(-EINVAL);
     162                 :            : 
     163                 :            :         /*
     164                 :            :          * Get the ldisc ops - we may need to request them to be loaded
     165                 :            :          * dynamically and try again.
     166                 :            :          */
     167                 :        579 :         ldops = get_ldops(disc);
     168         [ -  + ]:        579 :         if (IS_ERR(ldops)) {
     169                 :          0 :                 request_module("tty-ldisc-%d", disc);
     170                 :          0 :                 ldops = get_ldops(disc);
     171         [ #  # ]:          0 :                 if (IS_ERR(ldops))
     172                 :            :                         return ERR_CAST(ldops);
     173                 :            :         }
     174                 :            : 
     175                 :            :         ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);
     176         [ -  + ]:       1158 :         if (ld == NULL) {
     177                 :          0 :                 put_ldops(ldops);
     178                 :          0 :                 return ERR_PTR(-ENOMEM);
     179                 :            :         }
     180                 :            : 
     181                 :        579 :         ld->ops = ldops;
     182                 :        579 :         ld->tty = tty;
     183                 :            : 
     184                 :        579 :         return ld;
     185                 :            : }
     186                 :            : 
     187                 :            : /**
     188                 :            :  *      tty_ldisc_put           -       release the ldisc
     189                 :            :  *
     190                 :            :  *      Complement of tty_ldisc_get().
     191                 :            :  */
     192                 :            : static inline void tty_ldisc_put(struct tty_ldisc *ld)
     193                 :            : {
     194 [ #  # ][ #  # ]:        575 :         if (WARN_ON_ONCE(!ld))
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
           [ #  #  -  + ]
         [ #  # ][ #  # ]
           [ +  -  -  + ]
         [ #  # ][ #  # ]
         [ +  - ][ #  # ]
         [ #  # ][ #  # ]
           [ #  #  #  # ]
         [ #  # ][ #  # ]
           [ #  #  #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     195                 :            :                 return;
     196                 :            : 
     197                 :        575 :         put_ldops(ld->ops);
     198                 :        575 :         kfree(ld);
     199                 :            : }
     200                 :            : 
     201                 :          0 : static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos)
     202                 :            : {
     203         [ +  + ]:          2 :         return (*pos < NR_LDISCS) ? pos : NULL;
     204                 :            : }
     205                 :            : 
     206                 :          0 : static void *tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos)
     207                 :            : {
     208                 :         30 :         (*pos)++;
     209         [ +  + ]:         30 :         return (*pos < NR_LDISCS) ? pos : NULL;
     210                 :            : }
     211                 :            : 
     212                 :          0 : static void tty_ldiscs_seq_stop(struct seq_file *m, void *v)
     213                 :            : {
     214                 :          2 : }
     215                 :            : 
     216                 :          0 : static int tty_ldiscs_seq_show(struct seq_file *m, void *v)
     217                 :            : {
     218                 :         30 :         int i = *(loff_t *)v;
     219                 :            :         struct tty_ldisc_ops *ldops;
     220                 :            : 
     221                 :         30 :         ldops = get_ldops(i);
     222         [ +  + ]:         30 :         if (IS_ERR(ldops))
     223                 :            :                 return 0;
     224         [ +  - ]:          2 :         seq_printf(m, "%-10s %2d\n", ldops->name ? ldops->name : "???", i);
     225                 :          2 :         put_ldops(ldops);
     226                 :          2 :         return 0;
     227                 :            : }
     228                 :            : 
     229                 :            : static const struct seq_operations tty_ldiscs_seq_ops = {
     230                 :            :         .start  = tty_ldiscs_seq_start,
     231                 :            :         .next   = tty_ldiscs_seq_next,
     232                 :            :         .stop   = tty_ldiscs_seq_stop,
     233                 :            :         .show   = tty_ldiscs_seq_show,
     234                 :            : };
     235                 :            : 
     236                 :          0 : static int proc_tty_ldiscs_open(struct inode *inode, struct file *file)
     237                 :            : {
     238                 :          1 :         return seq_open(file, &tty_ldiscs_seq_ops);
     239                 :            : }
     240                 :            : 
     241                 :            : const struct file_operations tty_ldiscs_proc_fops = {
     242                 :            :         .owner          = THIS_MODULE,
     243                 :            :         .open           = proc_tty_ldiscs_open,
     244                 :            :         .read           = seq_read,
     245                 :            :         .llseek         = seq_lseek,
     246                 :            :         .release        = seq_release,
     247                 :            : };
     248                 :            : 
     249                 :            : /**
     250                 :            :  *      tty_ldisc_ref_wait      -       wait for the tty ldisc
     251                 :            :  *      @tty: tty device
     252                 :            :  *
     253                 :            :  *      Dereference the line discipline for the terminal and take a
     254                 :            :  *      reference to it. If the line discipline is in flux then
     255                 :            :  *      wait patiently until it changes.
     256                 :            :  *
     257                 :            :  *      Note: Must not be called from an IRQ/timer context. The caller
     258                 :            :  *      must also be careful not to hold other locks that will deadlock
     259                 :            :  *      against a discipline change, such as an existing ldisc reference
     260                 :            :  *      (which we check for)
     261                 :            :  *
     262                 :            :  *      Note: only callable from a file_operations routine (which
     263                 :            :  *      guarantees tty->ldisc != NULL when the lock is acquired).
     264                 :            :  */
     265                 :            : 
     266                 :          0 : struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
     267                 :            : {
     268                 :     239048 :         ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT);
     269         [ -  + ]:     239071 :         WARN_ON(!tty->ldisc);
     270                 :         23 :         return tty->ldisc;
     271                 :            : }
     272                 :            : EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
     273                 :            : 
     274                 :            : /**
     275                 :            :  *      tty_ldisc_ref           -       get the tty ldisc
     276                 :            :  *      @tty: tty device
     277                 :            :  *
     278                 :            :  *      Dereference the line discipline for the terminal and take a
     279                 :            :  *      reference to it. If the line discipline is in flux then
     280                 :            :  *      return NULL. Can be called from IRQ and timer functions.
     281                 :            :  */
     282                 :            : 
     283                 :          0 : struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
     284                 :            : {
     285                 :            :         struct tty_ldisc *ld = NULL;
     286                 :            : 
     287         [ +  - ]:      77574 :         if (ldsem_down_read_trylock(&tty->ldisc_sem)) {
     288                 :      77574 :                 ld = tty->ldisc;
     289         [ -  + ]:      77574 :                 if (!ld)
     290                 :          0 :                         ldsem_up_read(&tty->ldisc_sem);
     291                 :            :         }
     292                 :          0 :         return ld;
     293                 :            : }
     294                 :            : EXPORT_SYMBOL_GPL(tty_ldisc_ref);
     295                 :            : 
     296                 :            : /**
     297                 :            :  *      tty_ldisc_deref         -       free a tty ldisc reference
     298                 :            :  *      @ld: reference to free up
     299                 :            :  *
     300                 :            :  *      Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May
     301                 :            :  *      be called in IRQ context.
     302                 :            :  */
     303                 :            : 
     304                 :          0 : void tty_ldisc_deref(struct tty_ldisc *ld)
     305                 :            : {
     306                 :     316621 :         ldsem_up_read(&ld->tty->ldisc_sem);
     307                 :     316556 : }
     308                 :            : EXPORT_SYMBOL_GPL(tty_ldisc_deref);
     309                 :            : 
     310                 :            : 
     311                 :            : static inline int __lockfunc
     312                 :            : tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
     313                 :            : {
     314                 :        479 :         return ldsem_down_write(&tty->ldisc_sem, timeout);
     315                 :            : }
     316                 :            : 
     317                 :            : static inline int __lockfunc
     318                 :            : tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout)
     319                 :            : {
     320                 :        194 :         return ldsem_down_write_nested(&tty->ldisc_sem,
     321                 :            :                                        LDISC_SEM_OTHER, timeout);
     322                 :            : }
     323                 :            : 
     324                 :            : static inline void tty_ldisc_unlock(struct tty_struct *tty)
     325                 :            : {
     326                 :        673 :         return ldsem_up_write(&tty->ldisc_sem);
     327                 :            : }
     328                 :            : 
     329                 :            : static int __lockfunc
     330                 :          0 : tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2,
     331                 :            :                             unsigned long timeout)
     332                 :            : {
     333                 :            :         int ret;
     334                 :            : 
     335         [ +  + ]:        479 :         if (tty < tty2) {
     336                 :            :                 ret = tty_ldisc_lock(tty, timeout);
     337         [ +  - ]:         97 :                 if (ret) {
     338                 :            :                         ret = tty_ldisc_lock_nested(tty2, timeout);
     339         [ -  + ]:         97 :                         if (!ret)
     340                 :            :                                 tty_ldisc_unlock(tty);
     341                 :            :                 }
     342                 :            :         } else {
     343                 :            :                 /* if this is possible, it has lots of implications */
     344 [ -  + ][ #  # ]:        382 :                 WARN_ON_ONCE(tty == tty2);
                 [ #  # ]
     345         [ +  + ]:        382 :                 if (tty2 && tty != tty2) {
     346                 :            :                         ret = tty_ldisc_lock(tty2, timeout);
     347         [ +  - ]:         97 :                         if (ret) {
     348                 :            :                                 ret = tty_ldisc_lock_nested(tty, timeout);
     349         [ -  + ]:         97 :                                 if (!ret)
     350                 :            :                                         tty_ldisc_unlock(tty2);
     351                 :            :                         }
     352                 :            :                 } else
     353                 :            :                         ret = tty_ldisc_lock(tty, timeout);
     354                 :            :         }
     355                 :            : 
     356         [ +  - ]:        479 :         if (!ret)
     357                 :            :                 return -EBUSY;
     358                 :            : 
     359                 :        479 :         set_bit(TTY_LDISC_HALTED, &tty->flags);
     360         [ +  + ]:        479 :         if (tty2)
     361                 :        194 :                 set_bit(TTY_LDISC_HALTED, &tty2->flags);
     362                 :            :         return 0;
     363                 :            : }
     364                 :            : 
     365                 :            : static void __lockfunc
     366                 :            : tty_ldisc_lock_pair(struct tty_struct *tty, struct tty_struct *tty2)
     367                 :            : {
     368                 :        479 :         tty_ldisc_lock_pair_timeout(tty, tty2, MAX_SCHEDULE_TIMEOUT);
     369                 :            : }
     370                 :            : 
     371                 :            : static void __lockfunc tty_ldisc_unlock_pair(struct tty_struct *tty,
     372                 :            :                                              struct tty_struct *tty2)
     373                 :            : {
     374                 :            :         tty_ldisc_unlock(tty);
     375   [ +  +  +  - ]:        479 :         if (tty2)
     376                 :            :                 tty_ldisc_unlock(tty2);
     377                 :            : }
     378                 :            : 
     379                 :          0 : static void __lockfunc tty_ldisc_enable_pair(struct tty_struct *tty,
     380                 :            :                                              struct tty_struct *tty2)
     381                 :            : {
     382                 :         98 :         clear_bit(TTY_LDISC_HALTED, &tty->flags);
     383         [ +  - ]:         98 :         if (tty2)
     384                 :         98 :                 clear_bit(TTY_LDISC_HALTED, &tty2->flags);
     385                 :            : 
     386                 :            :         tty_ldisc_unlock_pair(tty, tty2);
     387                 :         98 : }
     388                 :            : 
     389                 :            : /**
     390                 :            :  *      tty_ldisc_flush -       flush line discipline queue
     391                 :            :  *      @tty: tty
     392                 :            :  *
     393                 :            :  *      Flush the line discipline queue (if any) for this tty. If there
     394                 :            :  *      is no line discipline active this is a no-op.
     395                 :            :  */
     396                 :            : 
     397                 :          0 : void tty_ldisc_flush(struct tty_struct *tty)
     398                 :            : {
     399                 :          0 :         struct tty_ldisc *ld = tty_ldisc_ref(tty);
     400         [ #  # ]:          0 :         if (ld) {
     401         [ #  # ]:          0 :                 if (ld->ops->flush_buffer)
     402                 :          0 :                         ld->ops->flush_buffer(tty);
     403                 :            :                 tty_ldisc_deref(ld);
     404                 :            :         }
     405                 :          0 :         tty_buffer_flush(tty);
     406                 :          0 : }
     407                 :            : EXPORT_SYMBOL_GPL(tty_ldisc_flush);
     408                 :            : 
     409                 :            : /**
     410                 :            :  *      tty_set_termios_ldisc           -       set ldisc field
     411                 :            :  *      @tty: tty structure
     412                 :            :  *      @num: line discipline number
     413                 :            :  *
     414                 :            :  *      This is probably overkill for real world processors but
     415                 :            :  *      they are not on hot paths so a little discipline won't do
     416                 :            :  *      any harm.
     417                 :            :  *
     418                 :            :  *      Locking: takes termios_rwsem
     419                 :            :  */
     420                 :            : 
     421                 :            : static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
     422                 :            : {
     423                 :        575 :         down_write(&tty->termios_rwsem);
     424                 :        575 :         tty->termios.c_line = num;
     425                 :        575 :         up_write(&tty->termios_rwsem);
     426                 :            : }
     427                 :            : 
     428                 :            : /**
     429                 :            :  *      tty_ldisc_open          -       open a line discipline
     430                 :            :  *      @tty: tty we are opening the ldisc on
     431                 :            :  *      @ld: discipline to open
     432                 :            :  *
     433                 :            :  *      A helper opening method. Also a convenient debugging and check
     434                 :            :  *      point.
     435                 :            :  *
     436                 :            :  *      Locking: always called with BTM already held.
     437                 :            :  */
     438                 :            : 
     439                 :          0 : static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
     440                 :            : {
     441         [ -  + ]:        579 :         WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
     442         [ +  - ]:        579 :         if (ld->ops->open) {
     443                 :            :                 int ret;
     444                 :            :                 /* BTM here locks versus a hangup event */
     445                 :        579 :                 ret = ld->ops->open(tty);
     446         [ -  + ]:        579 :                 if (ret)
     447                 :        579 :                         clear_bit(TTY_LDISC_OPEN, &tty->flags);
     448                 :            :                 return ret;
     449                 :            :         }
     450                 :            :         return 0;
     451                 :            : }
     452                 :            : 
     453                 :            : /**
     454                 :            :  *      tty_ldisc_close         -       close a line discipline
     455                 :            :  *      @tty: tty we are opening the ldisc on
     456                 :            :  *      @ld: discipline to close
     457                 :            :  *
     458                 :            :  *      A helper close method. Also a convenient debugging and check
     459                 :            :  *      point.
     460                 :            :  */
     461                 :            : 
     462                 :          0 : static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
     463                 :            : {
     464         [ -  + ]:        575 :         WARN_ON(!test_bit(TTY_LDISC_OPEN, &tty->flags));
     465                 :        575 :         clear_bit(TTY_LDISC_OPEN, &tty->flags);
     466         [ +  - ]:        575 :         if (ld->ops->close)
     467                 :        575 :                 ld->ops->close(tty);
     468                 :        575 : }
     469                 :            : 
     470                 :            : /**
     471                 :            :  *      tty_ldisc_restore       -       helper for tty ldisc change
     472                 :            :  *      @tty: tty to recover
     473                 :            :  *      @old: previous ldisc
     474                 :            :  *
     475                 :            :  *      Restore the previous line discipline or N_TTY when a line discipline
     476                 :            :  *      change fails due to an open error
     477                 :            :  */
     478                 :            : 
     479                 :          0 : static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
     480                 :            : {
     481                 :            :         char buf[64];
     482                 :            :         struct tty_ldisc *new_ldisc;
     483                 :            :         int r;
     484                 :            : 
     485                 :            :         /* There is an outstanding reference here so this is safe */
     486                 :          0 :         old = tty_ldisc_get(tty, old->ops->num);
     487         [ #  # ]:          0 :         WARN_ON(IS_ERR(old));
     488                 :          0 :         tty->ldisc = old;
     489                 :          0 :         tty_set_termios_ldisc(tty, old->ops->num);
     490         [ #  # ]:          0 :         if (tty_ldisc_open(tty, old) < 0) {
     491                 :            :                 tty_ldisc_put(old);
     492                 :            :                 /* This driver is always present */
     493                 :          0 :                 new_ldisc = tty_ldisc_get(tty, N_TTY);
     494         [ #  # ]:          0 :                 if (IS_ERR(new_ldisc))
     495                 :          0 :                         panic("n_tty: get");
     496                 :          0 :                 tty->ldisc = new_ldisc;
     497                 :            :                 tty_set_termios_ldisc(tty, N_TTY);
     498                 :          0 :                 r = tty_ldisc_open(tty, new_ldisc);
     499         [ #  # ]:          0 :                 if (r < 0)
     500                 :          0 :                         panic("Couldn't open N_TTY ldisc for "
     501                 :            :                               "%s --- error %d.",
     502                 :            :                               tty_name(tty, buf), r);
     503                 :            :         }
     504                 :          0 : }
     505                 :            : 
     506                 :            : /**
     507                 :            :  *      tty_set_ldisc           -       set line discipline
     508                 :            :  *      @tty: the terminal to set
     509                 :            :  *      @ldisc: the line discipline
     510                 :            :  *
     511                 :            :  *      Set the discipline of a tty line. Must be called from a process
     512                 :            :  *      context. The ldisc change logic has to protect itself against any
     513                 :            :  *      overlapping ldisc change (including on the other end of pty pairs),
     514                 :            :  *      the close of one side of a tty/pty pair, and eventually hangup.
     515                 :            :  */
     516                 :            : 
     517                 :          0 : int tty_set_ldisc(struct tty_struct *tty, int ldisc)
     518                 :            : {
     519                 :            :         int retval;
     520                 :          0 :         struct tty_ldisc *old_ldisc, *new_ldisc;
     521                 :          0 :         struct tty_struct *o_tty = tty->link;
     522                 :            : 
     523                 :          0 :         new_ldisc = tty_ldisc_get(tty, ldisc);
     524         [ #  # ]:          0 :         if (IS_ERR(new_ldisc))
     525                 :          0 :                 return PTR_ERR(new_ldisc);
     526                 :            : 
     527                 :          0 :         retval = tty_ldisc_lock_pair_timeout(tty, o_tty, 5 * HZ);
     528         [ #  # ]:          0 :         if (retval) {
     529                 :            :                 tty_ldisc_put(new_ldisc);
     530                 :          0 :                 return retval;
     531                 :            :         }
     532                 :            : 
     533                 :            :         /*
     534                 :            :          *      Check the no-op case
     535                 :            :          */
     536                 :            : 
     537         [ #  # ]:          0 :         if (tty->ldisc->ops->num == ldisc) {
     538                 :          0 :                 tty_ldisc_enable_pair(tty, o_tty);
     539                 :            :                 tty_ldisc_put(new_ldisc);
     540                 :            :                 return 0;
     541                 :            :         }
     542                 :            : 
     543                 :            :         old_ldisc = tty->ldisc;
     544                 :          0 :         tty_lock(tty);
     545                 :            : 
     546 [ #  # ][ #  # ]:          0 :         if (test_bit(TTY_HUPPING, &tty->flags) ||
     547                 :            :             test_bit(TTY_HUPPED, &tty->flags)) {
     548                 :            :                 /* We were raced by the hangup method. It will have stomped
     549                 :            :                    the ldisc data and closed the ldisc down */
     550                 :          0 :                 tty_ldisc_enable_pair(tty, o_tty);
     551                 :            :                 tty_ldisc_put(new_ldisc);
     552                 :          0 :                 tty_unlock(tty);
     553                 :          0 :                 return -EIO;
     554                 :            :         }
     555                 :            : 
     556                 :            :         /* Shutdown the old discipline. */
     557                 :          0 :         tty_ldisc_close(tty, old_ldisc);
     558                 :            : 
     559                 :            :         /* Now set up the new line discipline. */
     560                 :          0 :         tty->ldisc = new_ldisc;
     561                 :            :         tty_set_termios_ldisc(tty, ldisc);
     562                 :            : 
     563                 :          0 :         retval = tty_ldisc_open(tty, new_ldisc);
     564         [ #  # ]:          0 :         if (retval < 0) {
     565                 :            :                 /* Back to the old one or N_TTY if we can't */
     566                 :            :                 tty_ldisc_put(new_ldisc);
     567                 :          0 :                 tty_ldisc_restore(tty, old_ldisc);
     568                 :            :         }
     569                 :            : 
     570 [ #  # ][ #  # ]:          0 :         if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc)
     571                 :          0 :                 tty->ops->set_ldisc(tty);
     572                 :            : 
     573                 :            :         /* At this point we hold a reference to the new ldisc and a
     574                 :            :            reference to the old ldisc, or we hold two references to
     575                 :            :            the old ldisc (if it was restored as part of error cleanup
     576                 :            :            above). In either case, releasing a single reference from
     577                 :            :            the old ldisc is correct. */
     578                 :            : 
     579                 :            :         tty_ldisc_put(old_ldisc);
     580                 :            : 
     581                 :            :         /*
     582                 :            :          *      Allow ldisc referencing to occur again
     583                 :            :          */
     584                 :          0 :         tty_ldisc_enable_pair(tty, o_tty);
     585                 :            : 
     586                 :            :         /* Restart the work queue in case no characters kick it off. Safe if
     587                 :            :            already running */
     588                 :          0 :         schedule_work(&tty->port->buf.work);
     589         [ #  # ]:          0 :         if (o_tty)
     590                 :          0 :                 schedule_work(&o_tty->port->buf.work);
     591                 :            : 
     592                 :          0 :         tty_unlock(tty);
     593                 :          0 :         return retval;
     594                 :            : }
     595                 :            : 
     596                 :            : /**
     597                 :            :  *      tty_reset_termios       -       reset terminal state
     598                 :            :  *      @tty: tty to reset
     599                 :            :  *
     600                 :            :  *      Restore a terminal to the driver default state.
     601                 :            :  */
     602                 :            : 
     603                 :          0 : static void tty_reset_termios(struct tty_struct *tty)
     604                 :            : {
     605                 :         98 :         down_write(&tty->termios_rwsem);
     606                 :         98 :         tty->termios = tty->driver->init_termios;
     607                 :         98 :         tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios);
     608                 :         98 :         tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios);
     609                 :         98 :         up_write(&tty->termios_rwsem);
     610                 :         98 : }
     611                 :            : 
     612                 :            : 
     613                 :            : /**
     614                 :            :  *      tty_ldisc_reinit        -       reinitialise the tty ldisc
     615                 :            :  *      @tty: tty to reinit
     616                 :            :  *      @ldisc: line discipline to reinitialize
     617                 :            :  *
     618                 :            :  *      Switch the tty to a line discipline and leave the ldisc
     619                 :            :  *      state closed
     620                 :            :  */
     621                 :            : 
     622                 :          0 : static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
     623                 :            : {
     624                 :         98 :         struct tty_ldisc *ld = tty_ldisc_get(tty, ldisc);
     625                 :            : 
     626         [ +  - ]:         98 :         if (IS_ERR(ld))
     627                 :            :                 return -1;
     628                 :            : 
     629                 :         98 :         tty_ldisc_close(tty, tty->ldisc);
     630                 :         98 :         tty_ldisc_put(tty->ldisc);
     631                 :            :         /*
     632                 :            :          *      Switch the line discipline back
     633                 :            :          */
     634                 :         98 :         tty->ldisc = ld;
     635                 :            :         tty_set_termios_ldisc(tty, ldisc);
     636                 :            : 
     637                 :         98 :         return 0;
     638                 :            : }
     639                 :            : 
     640                 :            : /**
     641                 :            :  *      tty_ldisc_hangup                -       hangup ldisc reset
     642                 :            :  *      @tty: tty being hung up
     643                 :            :  *
     644                 :            :  *      Some tty devices reset their termios when they receive a hangup
     645                 :            :  *      event. In that situation we must also switch back to N_TTY properly
     646                 :            :  *      before we reset the termios data.
     647                 :            :  *
     648                 :            :  *      Locking: We can take the ldisc mutex as the rest of the code is
     649                 :            :  *      careful to allow for this.
     650                 :            :  *
     651                 :            :  *      In the pty pair case this occurs in the close() path of the
     652                 :            :  *      tty itself so we must be careful about locking rules.
     653                 :            :  */
     654                 :            : 
     655                 :          0 : void tty_ldisc_hangup(struct tty_struct *tty)
     656                 :            : {
     657                 :            :         struct tty_ldisc *ld;
     658                 :         98 :         int reset = tty->driver->flags & TTY_DRIVER_RESET_TERMIOS;
     659                 :            :         int err = 0;
     660                 :            : 
     661                 :            :         tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc);
     662                 :            : 
     663                 :         98 :         ld = tty_ldisc_ref(tty);
     664         [ +  - ]:         98 :         if (ld != NULL) {
     665         [ +  - ]:         98 :                 if (ld->ops->flush_buffer)
     666                 :         98 :                         ld->ops->flush_buffer(tty);
     667                 :         98 :                 tty_driver_flush_buffer(tty);
     668 [ -  + ][ #  # ]:         98 :                 if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
     669                 :          0 :                     ld->ops->write_wakeup)
     670                 :          0 :                         ld->ops->write_wakeup(tty);
     671         [ -  + ]:         98 :                 if (ld->ops->hangup)
     672                 :          0 :                         ld->ops->hangup(tty);
     673                 :            :                 tty_ldisc_deref(ld);
     674                 :            :         }
     675                 :            : 
     676                 :         98 :         wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
     677                 :         98 :         wake_up_interruptible_poll(&tty->read_wait, POLLIN);
     678                 :            : 
     679                 :         98 :         tty_unlock(tty);
     680                 :            : 
     681                 :            :         /*
     682                 :            :          * Shutdown the current line discipline, and reset it to
     683                 :            :          * N_TTY if need be.
     684                 :            :          *
     685                 :            :          * Avoid racing set_ldisc or tty_ldisc_release
     686                 :            :          */
     687                 :         98 :         tty_ldisc_lock_pair(tty, tty->link);
     688                 :         98 :         tty_lock(tty);
     689                 :            : 
     690         [ +  - ]:         98 :         if (tty->ldisc) {
     691                 :            : 
     692                 :            :                 /* At this point we have a halted ldisc; we want to close it and
     693                 :            :                    reopen a new ldisc. We could defer the reopen to the next
     694                 :            :                    open but it means auditing a lot of other paths so this is
     695                 :            :                    a FIXME */
     696         [ -  + ]:         98 :                 if (reset == 0) {
     697                 :            : 
     698         [ #  # ]:          0 :                         if (!tty_ldisc_reinit(tty, tty->termios.c_line))
     699                 :          0 :                                 err = tty_ldisc_open(tty, tty->ldisc);
     700                 :            :                         else
     701                 :            :                                 err = 1;
     702                 :            :                 }
     703                 :            :                 /* If the re-open fails or we reset then go to N_TTY. The
     704                 :            :                    N_TTY open cannot fail */
     705         [ +  - ]:         98 :                 if (reset || err) {
     706         [ -  + ]:         98 :                         BUG_ON(tty_ldisc_reinit(tty, N_TTY));
     707         [ -  + ]:         98 :                         WARN_ON(tty_ldisc_open(tty, tty->ldisc));
     708                 :            :                 }
     709                 :            :         }
     710                 :         98 :         tty_ldisc_enable_pair(tty, tty->link);
     711         [ +  - ]:         98 :         if (reset)
     712                 :         98 :                 tty_reset_termios(tty);
     713                 :            : 
     714                 :            :         tty_ldisc_debug(tty, "re-opened ldisc: %p\n", tty->ldisc);
     715                 :         98 : }
     716                 :            : 
     717                 :            : /**
     718                 :            :  *      tty_ldisc_setup                 -       open line discipline
     719                 :            :  *      @tty: tty being shut down
     720                 :            :  *      @o_tty: pair tty for pty/tty pairs
     721                 :            :  *
     722                 :            :  *      Called during the initial open of a tty/pty pair in order to set up the
     723                 :            :  *      line disciplines and bind them to the tty. This has no locking issues
     724                 :            :  *      as the device isn't yet active.
     725                 :            :  */
     726                 :            : 
     727                 :          0 : int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
     728                 :            : {
     729                 :        383 :         struct tty_ldisc *ld = tty->ldisc;
     730                 :            :         int retval;
     731                 :            : 
     732                 :        383 :         retval = tty_ldisc_open(tty, ld);
     733         [ +  - ]:        383 :         if (retval)
     734                 :            :                 return retval;
     735                 :            : 
     736         [ +  + ]:        383 :         if (o_tty) {
     737                 :         98 :                 retval = tty_ldisc_open(o_tty, o_tty->ldisc);
     738         [ -  + ]:         98 :                 if (retval) {
     739                 :          0 :                         tty_ldisc_close(tty, ld);
     740                 :          0 :                         return retval;
     741                 :            :                 }
     742                 :            :         }
     743                 :            :         return 0;
     744                 :            : }
     745                 :            : 
     746                 :          0 : static void tty_ldisc_kill(struct tty_struct *tty)
     747                 :            : {
     748                 :            :         /*
     749                 :            :          * Now kill off the ldisc
     750                 :            :          */
     751                 :        477 :         tty_ldisc_close(tty, tty->ldisc);
     752                 :        477 :         tty_ldisc_put(tty->ldisc);
     753                 :            :         /* Force an oops if we mess this up */
     754                 :        477 :         tty->ldisc = NULL;
     755                 :            : 
     756                 :            :         /* Ensure the next open requests the N_TTY ldisc */
     757                 :            :         tty_set_termios_ldisc(tty, N_TTY);
     758                 :        477 : }
     759                 :            : 
     760                 :            : /**
     761                 :            :  *      tty_ldisc_release               -       release line discipline
     762                 :            :  *      @tty: tty being shut down
     763                 :            :  *      @o_tty: pair tty for pty/tty pairs
     764                 :            :  *
     765                 :            :  *      Called during the final close of a tty/pty pair in order to shut down
     766                 :            :  *      the line discpline layer. On exit the ldisc assigned is N_TTY and the
     767                 :            :  *      ldisc has not been opened.
     768                 :            :  */
     769                 :            : 
     770                 :          0 : void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
     771                 :            : {
     772                 :            :         /*
     773                 :            :          * Shutdown this line discipline. As this is the final close,
     774                 :            :          * it does not race with the set_ldisc code path.
     775                 :            :          */
     776                 :            : 
     777                 :            :         tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc);
     778                 :            : 
     779                 :            :         tty_ldisc_lock_pair(tty, o_tty);
     780                 :        381 :         tty_lock_pair(tty, o_tty);
     781                 :            : 
     782                 :        381 :         tty_ldisc_kill(tty);
     783         [ +  + ]:        381 :         if (o_tty)
     784                 :         96 :                 tty_ldisc_kill(o_tty);
     785                 :            : 
     786                 :        381 :         tty_unlock_pair(tty, o_tty);
     787                 :            :         tty_ldisc_unlock_pair(tty, o_tty);
     788                 :            : 
     789                 :            :         /* And the memory resources remaining (buffers, termios) will be
     790                 :            :            disposed of when the kref hits zero */
     791                 :            : 
     792                 :            :         tty_ldisc_debug(tty, "ldisc closed\n");
     793                 :        381 : }
     794                 :            : 
     795                 :            : /**
     796                 :            :  *      tty_ldisc_init          -       ldisc setup for new tty
     797                 :            :  *      @tty: tty being allocated
     798                 :            :  *
     799                 :            :  *      Set up the line discipline objects for a newly allocated tty. Note that
     800                 :            :  *      the tty structure is not completely set up when this call is made.
     801                 :            :  */
     802                 :            : 
     803                 :          0 : void tty_ldisc_init(struct tty_struct *tty)
     804                 :            : {
     805                 :        481 :         struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
     806         [ -  + ]:        481 :         if (IS_ERR(ld))
     807                 :          0 :                 panic("n_tty: init_tty");
     808                 :        481 :         tty->ldisc = ld;
     809                 :        481 : }
     810                 :            : 
     811                 :            : /**
     812                 :            :  *      tty_ldisc_init          -       ldisc cleanup for new tty
     813                 :            :  *      @tty: tty that was allocated recently
     814                 :            :  *
     815                 :            :  *      The tty structure must not becompletely set up (tty_ldisc_setup) when
     816                 :            :  *      this call is made.
     817                 :            :  */
     818                 :          0 : void tty_ldisc_deinit(struct tty_struct *tty)
     819                 :            : {
     820                 :          0 :         tty_ldisc_put(tty->ldisc);
     821                 :          0 :         tty->ldisc = NULL;
     822                 :          0 : }
     823                 :            : 
     824                 :          0 : void tty_ldisc_begin(void)
     825                 :            : {
     826                 :            :         /* Setup the default TTY line discipline. */
     827                 :          0 :         (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
     828                 :          0 : }

Generated by: LCOV version 1.9