Print this page
    
NEX-15925 pseudonex, rootnex, and friends don't need to log useless device announcements
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/io/pseudonex.c
          +++ new/usr/src/uts/common/io/pseudonex.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
  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   * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23   23   * Use is subject to license terms.
  24   24   */
  25   25  
  26   26  
  27   27  /*
  28   28   * Pseudo devices are devices implemented entirely in software; pseudonex
  29   29   * (pseudo) is the traditional nexus for pseudodevices.  Instances are
  30   30   * typically specified via driver.conf files; e.g. a leaf device which
  31   31   * should be attached below pseudonex will have an entry like:
  32   32   *
  33   33   *      name="foo" parent="/pseudo" instance=0;
  34   34   *
  35   35   * pseudonex also supports the devctl (see <sys/devctl.h>) interface via
  36   36   * its :devctl minor node.  This allows priveleged userland applications to
  37   37   * online/offline children of pseudo as needed.
  38   38   *
  39   39   * In general, we discourage widespread use of this tactic, as it may lead to a
  40   40   * proliferation of nodes in /pseudo.  It is preferred that implementors update
  41   41   * pseudo.conf, adding another 'pseudo' nexus child of /pseudo, and then use
  42   42   * that for their collection of device nodes.  To do so, add a driver alias
  43   43   * for the name of the nexus child and a line in pseudo.conf such as:
  44   44   *
  45   45   *      name="foo" parent="/pseudo" instance=<n> valid-children="bar","baz";
  46   46   *
  47   47   * Setting 'valid-children' is important because we have an annoying problem;
  48   48   * we need to prevent pseudo devices with 'parent="pseudo"' set from binding
  49   49   * to our new pseudonex child node.  A better way might be to teach the
  50   50   * spec-node code to understand that parent="pseudo" really means
  51   51   * parent="/pseudo".
  52   52   *
  53   53   * At some point in the future, it would be desirable to extend the instance
  54   54   * database to include nexus children of pseudo.  Then we could use devctl
  55   55   * or devfs to online nexus children of pseudo, auto-selecting an instance #,
  56   56   * and the instance number selected would be preserved across reboot in
  57   57   * path_to_inst.
  58   58   */
  59   59  
  60   60  #include <sys/types.h>
  61   61  #include <sys/cmn_err.h>
  62   62  #include <sys/conf.h>
  63   63  #include <sys/ddi.h>
  64   64  #include <sys/ddi_impldefs.h>
  65   65  #include <sys/devops.h>
  66   66  #include <sys/instance.h>
  67   67  #include <sys/modctl.h>
  68   68  #include <sys/open.h>
  69   69  #include <sys/stat.h>
  70   70  #include <sys/sunddi.h>
  71   71  #include <sys/sunndi.h>
  72   72  #include <sys/systm.h>
  73   73  #include <sys/mkdev.h>
  74   74  
  75   75  /*
  76   76   * Config information
  77   77   */
  78   78  static int pseudonex_intr_op(dev_info_t *dip, dev_info_t *rdip,
  79   79              ddi_intr_op_t op, ddi_intr_handle_impl_t *hdlp, void *result);
  80   80  
  81   81  static int pseudonex_attach(dev_info_t *, ddi_attach_cmd_t);
  82   82  static int pseudonex_detach(dev_info_t *, ddi_detach_cmd_t);
  83   83  static int pseudonex_open(dev_t *, int, int, cred_t *);
  84   84  static int pseudonex_close(dev_t, int, int, cred_t *);
  85   85  static int pseudonex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
  86   86  static int pseudonex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
  87   87      void *);
  88   88  
  89   89  static void *pseudonex_state;
  90   90  
  91   91  typedef struct pseudonex_state {
  92   92          dev_info_t *pnx_devi;
  93   93  } pseudonex_state_t;
  94   94  
  95   95  static struct bus_ops pseudonex_bus_ops = {
  96   96          BUSO_REV,
  97   97          nullbusmap,             /* bus_map */
  98   98          NULL,                   /* bus_get_intrspec */
  99   99          NULL,                   /* bus_add_intrspec */
 100  100          NULL,                   /* bus_remove_intrspec */
 101  101          i_ddi_map_fault,        /* bus_map_fault */
 102  102          ddi_no_dma_map,         /* bus_dma_map */
 103  103          ddi_no_dma_allochdl,    /* bus_dma_allochdl */
 104  104          NULL,                   /* bus_dma_freehdl */
 105  105          NULL,                   /* bus_dma_bindhdl */
 106  106          NULL,                   /* bus_dma_unbindhdl */
 107  107          NULL,                   /* bus_dma_flush */
 108  108          NULL,                   /* bus_dma_win */
 109  109          NULL,                   /* bus_dma_ctl */
 110  110          pseudonex_ctl,          /* bus_ctl */
 111  111          ddi_bus_prop_op,        /* bus_prop_op */
 112  112          0,                      /* bus_get_eventcookie */
 113  113          0,                      /* bus_add_eventcall */
 114  114          0,                      /* bus_remove_eventcall */
 115  115          0,                      /* bus_post_event */
 116  116          NULL,                   /* bus_intr_ctl */
 117  117          NULL,                   /* bus_config */
 118  118          NULL,                   /* bus_unconfig */
 119  119          NULL,                   /* bus_fm_init */
 120  120          NULL,                   /* bus_fm_fini */
 121  121          NULL,                   /* bus_fm_access_enter */
 122  122          NULL,                   /* bus_fm_access_exit */
 123  123          NULL,                   /* bus_power */
 124  124          pseudonex_intr_op       /* bus_intr_op */
 125  125  };
 126  126  
 127  127  static struct cb_ops pseudonex_cb_ops = {
 128  128          pseudonex_open,                 /* open */
 129  129          pseudonex_close,                /* close */
 130  130          nodev,                          /* strategy */
 131  131          nodev,                          /* print */
 132  132          nodev,                          /* dump */
 133  133          nodev,                          /* read */
 134  134          nodev,                          /* write */
 135  135          pseudonex_ioctl,                /* ioctl */
 136  136          nodev,                          /* devmap */
 137  137          nodev,                          /* mmap */
 138  138          nodev,                          /* segmap */
 139  139          nochpoll,                       /* poll */
 140  140          ddi_prop_op,                    /* cb_prop_op */
 141  141          0,                              /* streamtab  */
 142  142          D_MP | D_NEW | D_HOTPLUG        /* Driver compatibility flag */
 143  143  };
 144  144  
 145  145  static struct dev_ops pseudo_ops = {
 146  146          DEVO_REV,               /* devo_rev, */
 147  147          0,                      /* refcnt  */
 148  148          ddi_getinfo_1to1,       /* info */
 149  149          nulldev,                /* identify */
 150  150          nulldev,                /* probe */
 151  151          pseudonex_attach,       /* attach */
 152  152          pseudonex_detach,       /* detach */
 153  153          nodev,                  /* reset */
 154  154          &pseudonex_cb_ops,      /* driver operations */
 155  155          &pseudonex_bus_ops,     /* bus operations */
 156  156          nulldev,                /* power */
 157  157          ddi_quiesce_not_needed,         /* quiesce */
 158  158  };
 159  159  
 160  160  /*
 161  161   * Module linkage information for the kernel.
 162  162   */
 163  163  static struct modldrv modldrv = {
 164  164          &mod_driverops,
 165  165          "nexus driver for 'pseudo' 1.31",
 166  166          &pseudo_ops,
 167  167  };
 168  168  
 169  169  static struct modlinkage modlinkage = {
 170  170          MODREV_1, (void *)&modldrv, NULL
 171  171  };
 172  172  
 173  173  int
 174  174  _init(void)
 175  175  {
 176  176          int err;
 177  177  
 178  178          if ((err = ddi_soft_state_init(&pseudonex_state,
 179  179              sizeof (pseudonex_state_t), 0)) != 0) {
 180  180                  return (err);
 181  181          }
 182  182          if ((err = mod_install(&modlinkage)) != 0) {
 183  183                  ddi_soft_state_fini(&pseudonex_state);
 184  184                  return (err);
 185  185          }
 186  186          return (0);
 187  187  }
 188  188  
 189  189  int
 190  190  _fini(void)
 191  191  {
 192  192          int err;
 193  193  
 194  194          if ((err = mod_remove(&modlinkage)) != 0)
 195  195                  return (err);
 196  196          ddi_soft_state_fini(&pseudonex_state);
 197  197          return (0);
 198  198  }
 199  199  
 200  200  int
 201  201  _info(struct modinfo *modinfop)
 202  202  {
 203  203          return (mod_info(&modlinkage, modinfop));
 204  204  }
 205  205  
 206  206  /*ARGSUSED*/
 207  207  static int
 208  208  pseudonex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 209  209  {
 210  210          int instance;
 211  211          pseudonex_state_t *pnx_state;
 212  212  
 213  213          switch (cmd) {
 214  214          case DDI_ATTACH:
 215  215                  break;
 216  216          case DDI_RESUME:
 217  217                  return (DDI_SUCCESS);
 218  218          default:
 219  219                  return (DDI_FAILURE);
 220  220          }
 221  221  
 222  222          /*
 223  223           * Save the devi for this instance in the soft_state data.
 224  224           */
 225  225          instance = ddi_get_instance(devi);
 226  226          if (ddi_soft_state_zalloc(pseudonex_state, instance) != DDI_SUCCESS)
 227  227                  return (DDI_FAILURE);
 228  228          pnx_state = ddi_get_soft_state(pseudonex_state, instance);
 229  229          pnx_state->pnx_devi = devi;
 230  230  
 231  231          if (ddi_create_minor_node(devi, "devctl", S_IFCHR, instance,
 232  232              DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
 233  233                  ddi_remove_minor_node(devi, NULL);
 234  234                  ddi_soft_state_free(pseudonex_state, instance);
 235  235                  return (DDI_FAILURE);
 236  236          }
 237  237          ddi_report_dev(devi);
 238  238          return (DDI_SUCCESS);
 239  239  }
 240  240  
 241  241  /*ARGSUSED*/
 242  242  static int
 243  243  pseudonex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 244  244  {
 245  245          int instance = ddi_get_instance(devi);
 246  246  
 247  247          if (cmd == DDI_SUSPEND)
 248  248                  return (DDI_SUCCESS);
 249  249  
 250  250          ddi_remove_minor_node(devi, NULL);
 251  251          ddi_soft_state_free(pseudonex_state, instance);
 252  252          return (DDI_SUCCESS);
 253  253  }
 254  254  
 255  255  /*ARGSUSED*/
 256  256  static int
 257  257  pseudonex_open(dev_t *devp, int flags, int otyp, cred_t *credp)
 258  258  {
 259  259          int instance;
 260  260  
 261  261          if (otyp != OTYP_CHR)
 262  262                  return (EINVAL);
 263  263  
 264  264          instance = getminor(*devp);
 265  265          if (ddi_get_soft_state(pseudonex_state, instance) == NULL)
 266  266                  return (ENXIO);
 267  267  
 268  268          return (0);
 269  269  }
 270  270  
 271  271  /*ARGSUSED*/
 272  272  static int
 273  273  pseudonex_close(dev_t dev, int flags, int otyp, cred_t *credp)
 274  274  {
 275  275          int instance;
 276  276  
 277  277          if (otyp != OTYP_CHR)
 278  278                  return (EINVAL);
 279  279  
 280  280          instance = getminor(dev);
 281  281          if (ddi_get_soft_state(pseudonex_state, instance) == NULL)
 282  282                  return (ENXIO);
 283  283  
 284  284          return (0);
 285  285  }
 286  286  
 287  287  /*ARGSUSED*/
 288  288  static int
 289  289  pseudonex_ioctl(dev_t dev,
 290  290      int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
 291  291  {
 292  292          int instance;
 293  293          pseudonex_state_t *pnx_state;
 294  294  
 295  295          instance = getminor(dev);
 296  296          if ((pnx_state = ddi_get_soft_state(pseudonex_state, instance)) == NULL)
 297  297                  return (ENXIO);
 298  298          ASSERT(pnx_state->pnx_devi);
 299  299          return (ndi_devctl_ioctl(pnx_state->pnx_devi, cmd, arg, mode, 0));
 300  300  }
 301  301  
 302  302  /*
 303  303   * pseudonex_intr_op: pseudonex convert an interrupt number to an
 304  304   *                      interrupt. NO OP for pseudo drivers.
 305  305   */
 306  306  /*ARGSUSED*/
 307  307  static int
 308  308  pseudonex_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op,
 309  309      ddi_intr_handle_impl_t *hdlp, void *result)
 310  310  {
 311  311          return (DDI_FAILURE);
 312  312  }
 313  313  
 314  314  static int
 315  315  pseudonex_check_assignment(dev_info_t *child, int test_inst)
 316  316  {
 317  317          dev_info_t      *tdip;
 318  318          kmutex_t        *dmp;
 319  319          const char      *childname = ddi_driver_name(child);
 320  320          major_t         childmaj = ddi_name_to_major((char *)childname);
 321  321  
 322  322          dmp = &devnamesp[childmaj].dn_lock;
 323  323          LOCK_DEV_OPS(dmp);
 324  324          for (tdip = devnamesp[childmaj].dn_head;
 325  325              tdip != NULL; tdip = ddi_get_next(tdip)) {
 326  326                  /* is this the current node? */
 327  327                  if (tdip == child)
 328  328                          continue;
 329  329                  /* is this a duplicate instance? */
 330  330                  if (test_inst == ddi_get_instance(tdip)) {
 331  331                          UNLOCK_DEV_OPS(dmp);
 332  332                          return (DDI_FAILURE);
 333  333                  }
 334  334          }
 335  335          UNLOCK_DEV_OPS(dmp);
 336  336          return (DDI_SUCCESS);
 337  337  }
 338  338  
 339  339  /*
 340  340   * This is a nasty, slow hack.  But we're stuck with it until we do some
 341  341   * major surgery on the instance assignment subsystem, to allow pseudonode
 342  342   * instance assignment to be tracked there.
 343  343   *
 344  344   * To auto-assign an instance number, we exhaustively search the instance
 345  345   * list for each possible instance number until we find one which is unused.
 346  346   */
 347  347  static int
 348  348  pseudonex_auto_assign(dev_info_t *child)
 349  349  {
 350  350          dev_info_t      *tdip;
 351  351          kmutex_t        *dmp;
 352  352          const char      *childname = ddi_driver_name(child);
 353  353          major_t         childmaj = ddi_name_to_major((char *)childname);
 354  354          int inst = 0;
 355  355  
 356  356          dmp = &devnamesp[childmaj].dn_lock;
 357  357          LOCK_DEV_OPS(dmp);
 358  358          for (inst = 0; inst <= MAXMIN32; inst++) {
 359  359                  for (tdip = devnamesp[childmaj].dn_head; tdip != NULL;
 360  360                      tdip = ddi_get_next(tdip)) {
 361  361                          /* is this the current node? */
 362  362                          if (tdip == child)
 363  363                                  continue;
 364  364                          if (inst == ddi_get_instance(tdip)) {
 365  365                                  break;
 366  366                          }
 367  367                  }
 368  368                  if (tdip == NULL) {
 369  369                          UNLOCK_DEV_OPS(dmp);
 370  370                          return (inst);
 371  371                  }
 372  372          }
 373  373          UNLOCK_DEV_OPS(dmp);
 374  374          return (-1);
  
    | 
      ↓ open down ↓ | 
    374 lines elided | 
    
      ↑ open up ↑ | 
  
 375  375  }
 376  376  
 377  377  static int
 378  378  pseudonex_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
 379  379      void *arg, void *result)
 380  380  {
 381  381          switch (ctlop) {
 382  382          case DDI_CTLOPS_REPORTDEV:
 383  383                  if (rdip == NULL)
 384  384                          return (DDI_FAILURE);
 385      -                cmn_err(CE_CONT, "?pseudo-device: %s%d\n",
 386      -                    ddi_driver_name(rdip), ddi_get_instance(rdip));
 387  385                  return (DDI_SUCCESS);
 388  386  
 389  387          case DDI_CTLOPS_INITCHILD:
 390  388          {
 391  389                  char name[12];  /* enough for a decimal integer */
 392  390                  int instance = -1;
 393  391                  dev_info_t *child = (dev_info_t *)arg;
 394  392                  const char *childname = ddi_driver_name(child);
 395  393                  char **childlist;
 396  394                  uint_t nelems;
 397  395                  int auto_assign = 0;
 398  396  
 399  397                  /*
 400  398                   * If this pseudonex node has a valid-children property,
 401  399                   * then that acts as an access control list for children
 402  400                   * allowed to attach beneath this node.  Honor it.
 403  401                   */
 404  402                  if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip,
 405  403                      DDI_PROP_DONTPASS, "valid-children", &childlist,
 406  404                      &nelems) == DDI_PROP_SUCCESS) {
 407  405                          int i, ok = 0;
 408  406                          for (i = 0; i < nelems; i++) {
 409  407                                  if (strcmp(childlist[i], childname) == 0) {
 410  408                                          ok = 1;
 411  409                                          break;
 412  410                                  }
 413  411                          }
 414  412                          ddi_prop_free(childlist);
 415  413                          if (!ok)
 416  414                                  return (DDI_FAILURE);
 417  415                  }
 418  416  
 419  417                  /*
 420  418                   * Look up the "instance" property. If it does not exist,
 421  419                   * check to see if the "auto-assign-instance" property is set.
 422  420                   * If not, default to using instance 0; while not ideal, this
 423  421                   * is a legacy behavior we must continue to support.
 424  422                   */
 425  423                  instance = ddi_prop_get_int(DDI_DEV_T_ANY, child,
 426  424                      DDI_PROP_DONTPASS, "instance", -1);
 427  425                  auto_assign = ddi_prop_exists(DDI_DEV_T_ANY, child,
 428  426                      DDI_PROP_DONTPASS, "auto-assign-instance");
 429  427                  NDI_CONFIG_DEBUG((CE_NOTE,
 430  428                      "pseudonex: DDI_CTLOPS_INITCHILD(instance=%d, "
 431  429                      "auto-assign=%d)", instance, auto_assign));
 432  430  
 433  431                  if (instance != -1 && auto_assign != 0) {
 434  432                          NDI_CONFIG_DEBUG((CE_NOTE, "both instance and "
 435  433                              "auto-assign-instance properties specified. "
 436  434                              "Node rejected."));
 437  435                          return (DDI_FAILURE);
 438  436                  }
 439  437  
 440  438                  if (instance == -1 && auto_assign == 0) {
 441  439                          /* default to instance 0 if not specified */
 442  440                          NDI_CONFIG_DEBUG((CE_NOTE, "defaulting to 0"));
 443  441                          instance = 0;
 444  442                  }
 445  443  
 446  444                  /*
 447  445                   * If an instance has been specified, determine if this
 448  446                   * instance is already in use; if we need to pick an instance,
 449  447                   * we do it here.
 450  448                   */
 451  449                  if (auto_assign) {
 452  450                          if ((instance = pseudonex_auto_assign(child)) == -1) {
 453  451                                  NDI_CONFIG_DEBUG((CE_NOTE, "failed to "
 454  452                                      "auto-select instance for %s", childname));
 455  453                                  return (DDI_FAILURE);
 456  454                          }
 457  455                          NDI_CONFIG_DEBUG((CE_NOTE,
 458  456                              "auto-selected instance for %s: %d",
 459  457                              childname, instance));
 460  458                  } else {
 461  459                          if (pseudonex_check_assignment(child, instance) ==
 462  460                              DDI_FAILURE) {
 463  461                                  NDI_CONFIG_DEBUG((CE_WARN,
 464  462                                      "Duplicate instance %d of node \"%s\" "
 465  463                                      "ignored.", instance, childname));
 466  464                                  return (DDI_FAILURE);
 467  465                          }
 468  466                          NDI_CONFIG_DEBUG((CE_NOTE,
 469  467                              "using fixed-assignment instance for %s: %d",
 470  468                              childname, instance));
 471  469                  }
 472  470  
 473  471                  /*
 474  472                   * Attach the instance number to the node. This allows
 475  473                   * us to have multiple instances of the same pseudo
 476  474                   * device, they will be named 'device@instance'. If this
 477  475                   * breaks programs, we may need to special-case instance 0
 478  476                   * into 'device'. Ick. devlinks appears to handle the
 479  477                   * new names ok, so if only names in /dev are used
 480  478                   * this may not be necessary.
 481  479                   */
 482  480                  (void) snprintf(name, sizeof (name), "%d", instance);
 483  481                  DEVI(child)->devi_instance = instance;
 484  482                  ddi_set_name_addr(child, name);
 485  483                  return (DDI_SUCCESS);
 486  484          }
 487  485  
 488  486          case DDI_CTLOPS_UNINITCHILD:
 489  487          {
 490  488                  dev_info_t *child = (dev_info_t *)arg;
 491  489  
 492  490                  NDI_CONFIG_DEBUG((CE_NOTE,
 493  491                      "DDI_CTLOPS_UNINITCHILD(%s, instance=%d)",
 494  492                      ddi_driver_name(child), DEVI(child)->devi_instance));
 495  493  
 496  494                  ddi_set_name_addr(child, NULL);
 497  495  
 498  496                  return (DDI_SUCCESS);
 499  497          }
 500  498  
 501  499          case DDI_CTLOPS_DMAPMAPC:
 502  500          case DDI_CTLOPS_REPORTINT:
 503  501          case DDI_CTLOPS_REGSIZE:
 504  502          case DDI_CTLOPS_NREGS:
 505  503          case DDI_CTLOPS_SIDDEV:
 506  504          case DDI_CTLOPS_SLAVEONLY:
 507  505          case DDI_CTLOPS_AFFINITY:
 508  506          case DDI_CTLOPS_POKE:
 509  507          case DDI_CTLOPS_PEEK:
 510  508                  /*
 511  509                   * These ops correspond to functions that "shouldn't" be called
 512  510                   * by a pseudo driver.  So we whine when we're called.
 513  511                   */
 514  512                  cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
 515  513                      ddi_driver_name(dip), ddi_get_instance(dip), ctlop,
 516  514                      ddi_driver_name(rdip), ddi_get_instance(rdip));
 517  515                  return (DDI_FAILURE);
 518  516  
 519  517          case DDI_CTLOPS_ATTACH:
 520  518          case DDI_CTLOPS_BTOP:
 521  519          case DDI_CTLOPS_BTOPR:
 522  520          case DDI_CTLOPS_DETACH:
 523  521          case DDI_CTLOPS_DVMAPAGESIZE:
 524  522          case DDI_CTLOPS_IOMIN:
 525  523          case DDI_CTLOPS_POWER:
 526  524          case DDI_CTLOPS_PTOB:
 527  525          default:
 528  526                  /*
 529  527                   * The ops that we pass up (default).  We pass up memory
 530  528                   * allocation oriented ops that we receive - these may be
 531  529                   * associated with pseudo HBA drivers below us with target
 532  530                   * drivers below them that use ddi memory allocation
 533  531                   * interfaces like scsi_alloc_consistent_buf.
 534  532                   */
 535  533                  return (ddi_ctlops(dip, rdip, ctlop, arg, result));
 536  534          }
 537  535  }
  
    | 
      ↓ open down ↓ | 
    141 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX