Print this page
    
OS-3752 Increase IOV_MAX to at least 1024
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/io/cons.c
          +++ new/usr/src/uts/common/io/cons.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  
    | 
      ↓ open down ↓ | 
    13 lines elided | 
    
      ↑ open up ↑ | 
  
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 1982, 2010, Oracle and/or its affiliates. All rights reserved.
       24 + * Copyright 2015, Joyent, Inc. All rights reserved.
  24   25   */
  25   26  
  26   27  /*
  27   28   * Indirect console driver for Sun.
  28   29   *
  29   30   * Redirects all I/O to the device designated as the underlying "hardware"
  30   31   * console, as given by the value of rconsvp.  The implementation assumes that
  31   32   * rconsvp denotes a STREAMS device; the assumption is justified since
  32   33   * consoles must be capable of effecting tty semantics.
  33   34   *
  34   35   * rconsvp is set in autoconf.c:consconfig(), based on information obtained
  35   36   * from the EEPROM.
  36   37   *
  37   38   * XXX: The driver still needs to be converted to use ANSI C consistently
  38   39   *      throughout.
  39   40   */
  40   41  
  41   42  #include <sys/types.h>
  42   43  #include <sys/open.h>
  43   44  #include <sys/param.h>
  44   45  #include <sys/systm.h>
  45   46  #include <sys/signal.h>
  
    | 
      ↓ open down ↓ | 
    12 lines elided | 
    
      ↑ open up ↑ | 
  
  46   47  #include <sys/cred.h>
  47   48  #include <sys/user.h>
  48   49  #include <sys/proc.h>
  49   50  #include <sys/disp.h>
  50   51  #include <sys/file.h>
  51   52  #include <sys/taskq.h>
  52   53  #include <sys/log.h>
  53   54  #include <sys/vnode.h>
  54   55  #include <sys/uio.h>
  55   56  #include <sys/stat.h>
       57 +#include <sys/limits.h>
  56   58  
  57   59  #include <sys/console.h>
  58   60  #include <sys/consdev.h>
  59   61  
  60   62  #include <sys/stream.h>
  61   63  #include <sys/strsubr.h>
  62   64  #include <sys/poll.h>
  63   65  
  64   66  #include <sys/debug.h>
  65   67  
  66   68  #include <sys/conf.h>
  67   69  #include <sys/ddi.h>
  68   70  #include <sys/sunddi.h>
  69   71  #include <sys/vt.h>
  70   72  
  71   73  static int cnopen(dev_t *, int, int, struct cred *);
  72   74  static int cnclose(dev_t, int, int, struct cred *);
  73   75  static int cnread(dev_t, struct uio *, struct cred *);
  74   76  static int cnwrite(dev_t, struct uio *, struct cred *);
  75   77  static int cnioctl(dev_t, int, intptr_t, int, struct cred *, int *);
  76   78  static int cnpoll(dev_t, short, int, short *, struct pollhead **);
  77   79  static int cn_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
  78   80  static int cn_attach(dev_info_t *, ddi_attach_cmd_t);
  79   81  static int cn_detach(dev_info_t *, ddi_detach_cmd_t);
  80   82  
  81   83  static dev_info_t *cn_dip;              /* private copy of devinfo pointer */
  82   84  
  83   85  static struct cb_ops cn_cb_ops = {
  84   86  
  85   87          cnopen,                 /* open */
  86   88          cnclose,                /* close */
  87   89          nodev,                  /* strategy */
  88   90          nodev,                  /* print */
  89   91          nodev,                  /* dump */
  90   92          cnread,                 /* read */
  91   93          cnwrite,                /* write */
  92   94          cnioctl,                /* ioctl */
  93   95          nodev,                  /* devmap */
  94   96          nodev,                  /* mmap */
  95   97          nodev,                  /* segmap */
  96   98          cnpoll,                 /* poll */
  97   99          ddi_prop_op,            /* cb_prop_op */
  98  100          0,                      /* streamtab  */
  99  101          D_NEW | D_MP            /* Driver compatibility flag */
 100  102  
 101  103  };
 102  104  
 103  105  static struct dev_ops cn_ops = {
 104  106  
 105  107          DEVO_REV,               /* devo_rev, */
 106  108          0,                      /* refcnt  */
 107  109          cn_info,                /* info */
 108  110          nulldev,                /* identify */
 109  111          nulldev,                /* probe */
 110  112          cn_attach,              /* attach */
 111  113          cn_detach,              /* detach */
 112  114          nodev,                  /* reset */
 113  115          &cn_cb_ops,             /* driver operations */
 114  116          (struct bus_ops *)0,    /* bus operations */
 115  117          NULL,                   /* power */
 116  118          ddi_quiesce_not_needed,         /* quiesce */
 117  119  
 118  120  };
 119  121  
 120  122  /*
 121  123   * Global variables associated with the console device:
 122  124   *
 123  125   * XXX: There are too many of these!
 124  126   * moved to space.c to become resident in the kernel so that cons
 125  127   * can be loadable.
 126  128   */
 127  129  
 128  130  extern dev_t    rconsdev;       /* "hardware" console */
 129  131  extern vnode_t  *rconsvp;       /* pointer to vnode for that device */
 130  132  
 131  133  /*
 132  134   * XXX: consulted in prsubr.c, for /proc entry point for obtaining ps info.
 133  135   */
 134  136  extern dev_t    uconsdev;       /* What the user thinks is the console device */
 135  137  
 136  138  /*
 137  139   * Private driver state:
 138  140   */
 139  141  
 140  142  /*
 141  143   * The underlying console device potentially can be opened through (at least)
 142  144   * two paths: through this driver and through the underlying device's driver.
 143  145   * To ensure that reference counts are meaningful and therefore that close
 144  146   * routines are called at the right time, it's important to make sure that
 145  147   * rconsvp's s_count field (i.e., the count on the underlying device) never
 146  148   * has a contribution of more than one through this driver, regardless of how
 147  149   * many times this driver's been opened.  rconsopen keeps track of the
 148  150   * necessary information to ensure this property.
 149  151   */
 150  152  static uint_t   rconsopen;
 151  153  
 152  154  
 153  155  #include <sys/types.h>
 154  156  #include <sys/conf.h>
 155  157  #include <sys/param.h>
 156  158  #include <sys/systm.h>
 157  159  #include <sys/errno.h>
 158  160  #include <sys/modctl.h>
 159  161  
 160  162  
 161  163  extern int nodev(), nulldev();
 162  164  extern int dseekneg_flag;
 163  165  extern struct mod_ops mod_driverops;
 164  166  extern struct dev_ops cn_ops;
 165  167  
 166  168  /*
 167  169   * Module linkage information for the kernel.
 168  170   */
 169  171  
 170  172  static struct modldrv modldrv = {
 171  173          &mod_driverops, /* Type of module.  This one is a pseudo driver */
 172  174          "Console redirection driver",
 173  175          &cn_ops,        /* driver ops */
 174  176  };
 175  177  
 176  178  static struct modlinkage modlinkage = {
 177  179          MODREV_1,
 178  180          &modldrv,
 179  181          NULL
 180  182  };
 181  183  
 182  184  int
 183  185  _init(void)
 184  186  {
 185  187          return (mod_install(&modlinkage));
 186  188  }
 187  189  
 188  190  int
 189  191  _fini(void)
 190  192  {
 191  193          return (EBUSY);
 192  194  }
 193  195  
 194  196  int
 195  197  _info(struct modinfo *modinfop)
 196  198  {
 197  199          return (mod_info(&modlinkage, modinfop));
 198  200  }
 199  201  
 200  202  /*
 201  203   * DDI glue routines
 202  204   */
 203  205  static int
 204  206  cn_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 205  207  {
 206  208          if (cmd != DDI_ATTACH)
 207  209                  return (DDI_FAILURE);
 208  210  
 209  211          if (ddi_create_minor_node(devi, "syscon", S_IFCHR,
 210  212              0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
 211  213                  return (DDI_FAILURE);
 212  214          }
 213  215          if (ddi_create_minor_node(devi, "systty", S_IFCHR,
 214  216              0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
 215  217                  ddi_remove_minor_node(devi, NULL);
 216  218                  return (DDI_FAILURE);
 217  219          }
 218  220          if (ddi_create_minor_node(devi, "console", S_IFCHR,
 219  221              0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
 220  222                  ddi_remove_minor_node(devi, NULL);
 221  223                  return (DDI_FAILURE);
 222  224          }
 223  225  
 224  226          cn_dip = devi;
 225  227          return (DDI_SUCCESS);
 226  228  }
 227  229  
 228  230  static int
 229  231  cn_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 230  232  {
 231  233          if (cmd != DDI_DETACH)
 232  234                  return (DDI_FAILURE);
 233  235          ddi_remove_minor_node(devi, NULL);
 234  236          uconsdev = NODEV;
 235  237          return (DDI_SUCCESS);
 236  238  }
 237  239  
 238  240  /* ARGSUSED */
 239  241  static int
 240  242  cn_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 241  243  {
 242  244          int error = DDI_FAILURE;
 243  245  
 244  246          switch (infocmd) {
 245  247          case DDI_INFO_DEVT2DEVINFO:
 246  248                  if (getminor((dev_t)arg) == 0 && cn_dip != NULL) {
 247  249                          *result = (void *) cn_dip;
 248  250                          error = DDI_SUCCESS;
 249  251                  }
 250  252                  break;
 251  253  
 252  254          case DDI_INFO_DEVT2INSTANCE:
 253  255                  if (getminor((dev_t)arg) == 0) {
 254  256                          *result = (void *)0;
 255  257                          error = DDI_SUCCESS;
 256  258                  }
 257  259                  break;
 258  260  
 259  261          default:
 260  262                  break;
 261  263          }
 262  264  
 263  265          return (error);
 264  266  }
 265  267  
 266  268  /*
 267  269   * XXX  Caution: before allowing more than 256 minor devices on the
 268  270   *      console, make sure you understand the 'compatibility' hack
 269  271   *      in ufs_iget() that translates old dev_t's to new dev_t's.
 270  272   *      See bugid 1098104 for the sordid details.
 271  273   */
 272  274  
 273  275  /* ARGSUSED */
 274  276  static int
 275  277  cnopen(dev_t *dev, int flag, int state, struct cred *cred)
 276  278  {
 277  279          int     err;
 278  280          static int      been_here;
 279  281          vnode_t *vp = rconsvp;
 280  282  
 281  283          ASSERT(cred != NULL);
 282  284  
 283  285          if (rconsvp == NULL)
 284  286                  return (0);
 285  287  
 286  288          /*
 287  289           * Enable virtual console I/O for console logging if needed.
 288  290           */
 289  291          if (vsconsvp != NULL && vsconsvp->v_stream == NULL) {
 290  292                  if (VOP_OPEN(&vsconsvp, FREAD | FWRITE, cred, NULL) != 0) {
 291  293                          cmn_err(CE_WARN, "cnopen: failed to open vsconsvp "
 292  294                              "for virtual console logging");
 293  295                  }
 294  296          }
 295  297  
 296  298          /*
 297  299           * XXX: Clean up inactive PIDs from previous opens if any.
 298  300           * These would have been created as a result of an I_SETSIG
 299  301           * issued against console.  This is a workaround, and
 300  302           * console driver must be correctly redesigned not to need
 301  303           * this hook.
 302  304           */
 303  305          if (vp->v_stream) {
 304  306                  str_cn_clean(vp);
 305  307          }
 306  308  
 307  309          /*
 308  310           * XXX: Set hook to tell /proc about underlying console.  (There's
 309  311           *      gotta be a better way...)
 310  312           */
 311  313          if (state != OTYP_CHR || getminor(*dev) != 0)
 312  314                  return (ENXIO);
 313  315          if (been_here == 0) {
 314  316                  uconsdev = *dev;
 315  317                  been_here = 1;
 316  318                  if (vn_open("/dev/console", UIO_SYSSPACE, FWRITE | FNOCTTY,
 317  319                      0, &console_vnode, 0, 0) == 0)
 318  320                          console_taskq = taskq_create("console_taskq",
 319  321                              1, maxclsyspri - 1, LOG_LOWAT / LOG_MSGSIZE,
 320  322                              LOG_HIWAT / LOG_MSGSIZE, TASKQ_PREPOPULATE);
 321  323          }
 322  324  
 323  325          if ((err = VOP_OPEN(&vp, flag, cred, NULL)) != 0)
 324  326                  return (err);
 325  327  
 326  328          /*
 327  329           * The underlying driver is not allowed to have cloned itself
 328  330           * for this open.
 329  331           */
 330  332          if (vp != rconsvp) {
 331  333                  /*
 332  334                   * It might happen that someone set rconsvp to NULL
 333  335                   * whilst we were in the middle of the open.
 334  336                   */
 335  337                  if (rconsvp == NULL) {
 336  338                          (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
 337  339                          return (0);
 338  340                  }
 339  341                  cmn_err(CE_PANIC, "cnopen: cloned open");
 340  342          }
 341  343  
 342  344          rconsopen++;
 343  345  
 344  346          return (0);
 345  347  }
 346  348  
 347  349  /* ARGSUSED */
 348  350  static int
 349  351  cnclose(dev_t dev, int flag, int state, struct cred *cred)
 350  352  {
 351  353          int     err = 0;
 352  354          vnode_t *vp;
 353  355  
 354  356          /*
 355  357           * Since this is the _last_ close, it's our last chance to close the
 356  358           * underlying device.  (Note that if someone else has the underlying
 357  359           * hardware console device open, we won't get here, since spec_close
 358  360           * will see s_count > 1.)
 359  361           */
 360  362          if (state != OTYP_CHR)
 361  363                  return (ENXIO);
 362  364  
 363  365          if (rconsvp == NULL)
 364  366                  return (0);
 365  367  
 366  368          while ((rconsopen != 0) && ((vp = rconsvp) != NULL)) {
 367  369                  err = VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL);
 368  370                  if (!err) {
 369  371                          rconsopen--;
 370  372                  }
 371  373          }
 372  374          return (err);
 373  375  }
 374  376  
 375  377  /* ARGSUSED */
 376  378  static int
 377  379  cnread(dev_t dev, struct uio *uio, struct cred *cred)
 378  380  {
 379  381          kcondvar_t      sleep_forever;
 380  382          kmutex_t        sleep_forever_mutex;
 381  383  
 382  384          if (rconsvp == NULL) {
 383  385                  /*
 384  386                   * Go to sleep forever.  This seems like the least
 385  387                   * harmful thing to do if there's no console.
 386  388                   * EOF might be better if we're ending up single-user
 387  389                   * mode.
 388  390                   */
 389  391                  cv_init(&sleep_forever, NULL, CV_DRIVER, NULL);
 390  392                  mutex_init(&sleep_forever_mutex, NULL, MUTEX_DRIVER, NULL);
 391  393                  mutex_enter(&sleep_forever_mutex);
 392  394                  (void) cv_wait_sig(&sleep_forever, &sleep_forever_mutex);
 393  395                  mutex_exit(&sleep_forever_mutex);
 394  396                  return (EIO);
 395  397          }
 396  398  
 397  399          if (rconsvp->v_stream != NULL)
 398  400                  return (strread(rconsvp, uio, cred));
 399  401          else
 400  402                  return (cdev_read(rconsdev, uio, cred));
 401  403  }
 402  404  
 403  405  /* ARGSUSED */
 404  406  static int
 405  407  cnwrite(dev_t dev, struct uio *uio, struct cred *cred)
 406  408  {
  
    | 
      ↓ open down ↓ | 
    341 lines elided | 
    
      ↑ open up ↑ | 
  
 407  409          if (rconsvp == NULL) {
 408  410                  uio->uio_resid = 0;
 409  411                  return (0);
 410  412          }
 411  413  
 412  414          /*
 413  415           * Output to virtual console for logging if enabled.
 414  416           */
 415  417          if (vsconsvp != NULL && vsconsvp->v_stream != NULL) {
 416  418                  struiod_t uiod;
      419 +                struct iovec buf[IOV_MAX_STACK];
      420 +                int iovlen = 0;
 417  421  
      422 +                if (uio->uio_iovcnt > IOV_MAX_STACK) {
      423 +                        iovlen = uio->uio_iovcnt * sizeof (iovec_t);
      424 +                        uiod.d_iov = kmem_alloc(iovlen, KM_SLEEP);
      425 +                } else {
      426 +                        uiod.d_iov = buf;
      427 +                }
      428 +
 418  429                  /*
 419  430                   * strwrite modifies uio so need to make copy.
 420  431                   */
 421      -                (void) uiodup(uio, &uiod.d_uio, uiod.d_iov,
 422      -                    sizeof (uiod.d_iov) / sizeof (*uiod.d_iov));
      432 +                (void) uiodup(uio, &uiod.d_uio, uiod.d_iov, uio->uio_iovcnt);
 423  433  
 424  434                  (void) strwrite(vsconsvp, &uiod.d_uio, cred);
      435 +                if (iovlen != 0)
      436 +                        kmem_free(uiod.d_iov, iovlen);
 425  437          }
 426  438  
 427  439          if (rconsvp->v_stream != NULL)
 428  440                  return (strwrite(rconsvp, uio, cred));
 429  441          else
 430  442                  return (cdev_write(rconsdev, uio, cred));
 431  443  }
 432  444  
 433  445  /* ARGSUSED */
 434  446  static int
 435  447  cnprivateioc(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred,
 436  448          int *rvalp)
 437  449  {
 438  450  
 439  451          /* currently we only support one ioctl */
 440  452          if (cmd != CONS_GETTERM)
 441  453                  return (EINVAL);
 442  454  
 443  455          /* Confirm iwscn is immediate target of cn redirection */
 444  456          if (rconsvp != wsconsvp)
 445  457                  return (ENODEV);
 446  458  
 447  459          /*
 448  460           * If the redirection client is not wc, it should return
 449  461           * error upon receiving the CONS_GETTERM ioctl.
 450  462           *
 451  463           * if it is wc, we know that the target supports the CONS_GETTERM
 452  464           * ioctl, which very conviently has the exact same data
 453  465           * format as this ioctl...  so let's just pass it on.
 454  466           */
 455  467          return (cdev_ioctl(rconsdev, CONS_GETTERM, arg, flag, cred, rvalp));
 456  468  }
 457  469  
 458  470  /* ARGSUSED */
 459  471  static int
 460  472  cnioctl(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred,
 461  473          int *rvalp)
 462  474  {
 463  475          if (rconsvp == NULL)
 464  476                  return (0);
 465  477  
 466  478          /*
 467  479           * In wc, VT_SET_CONSUSER which comes from minor node 0
 468  480           * has two sources -- either /dev/console or /dev/vt/0 .
 469  481           * We need a way to differentiate them, so here we
 470  482           * change VT_SET_CONSUSER to a private VT_RESET_CONSUSER
 471  483           * ioctl.
 472  484           */
 473  485          if (cmd == VT_SET_CONSUSER)
 474  486                  cmd = VT_RESET_CONSUSER;
 475  487  
 476  488          if ((cmd & _CNIOC_MASK) == _CNIOC)
 477  489                  return (cnprivateioc(dev, cmd, arg, flag, cred, rvalp));
 478  490  
 479  491          if (rconsvp->v_stream != NULL)
 480  492                  return (strioctl(rconsvp, cmd, arg, flag, U_TO_K,
 481  493                      cred, rvalp));
 482  494  
 483  495          return (cdev_ioctl(rconsdev, cmd, arg, flag, cred, rvalp));
 484  496  }
 485  497  
 486  498  /* ARGSUSED */
 487  499  static int
 488  500  cnpoll(dev_t dev, short events, int anyyet, short *reventsp,
 489  501          struct pollhead **phpp)
 490  502  {
 491  503          if (rconsvp == NULL)
 492  504                  return (nochpoll(dev, events, anyyet, reventsp, phpp));
 493  505  
 494  506          if (rconsvp->v_stream != NULL)
 495  507                  return (strpoll(rconsvp->v_stream, events, anyyet, reventsp,
 496  508                      phpp));
 497  509          else
 498  510                  return (cdev_poll(rconsdev, events, anyyet, reventsp, phpp));
 499  511  }
  
    | 
      ↓ open down ↓ | 
    65 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX