LCOV - code coverage report
Current view: top level - drivers/tty - tty_ldisc.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 120 276 43.5 %
Date: 2022-03-28 16:04:14 Functions: 17 27 63.0 %
Branches: 25 128 19.5 %

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

Generated by: LCOV version 1.14