Diff for /src/sys/kern/kern_conf.c between versions 1.7 and 1.8

version 1.7, 2004/05/13 00:23:39 version 1.8, 2004/05/19 22:52:58
Line 61  MALLOC_DEFINE(M_DEVT, "dev_t", "dev_t st Line 61  MALLOC_DEFINE(M_DEVT, "dev_t", "dev_t st
 #define DEVT_STASH 50  #define DEVT_STASH 50
   
 static struct specinfo devt_stash[DEVT_STASH];  static struct specinfo devt_stash[DEVT_STASH];
   
 static LIST_HEAD(, specinfo) dev_hash[DEVT_HASH];  static LIST_HEAD(, specinfo) dev_hash[DEVT_HASH];
   static LIST_HEAD(, specinfo) dev_free_list;
 static LIST_HEAD(, specinfo) dev_free;  
   
 static int free_devt;  static int free_devt;
 SYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");  SYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");
   int dev_ref_debug = 0;
   SYSCTL_INT(_debug, OID_AUTO, dev_refs, CTLFLAG_RW, &dev_ref_debug, 0, "");
   
 /*  /*
  * dev_t and u_dev_t primitives   * dev_t and u_dev_t primitives.  Note that the major number is always
    * extracted from si_udev, not from si_devsw, because si_devsw is replaced
    * when a device is destroyed.
  */   */
   
 int  int
 major(dev_t x)  major(dev_t x)
 {  {
Line 100  lminor(dev_t x) Line 101  lminor(dev_t x)
         return ((i & 0xff) | (i >> 8));          return ((i & 0xff) | (i >> 8));
 }  }
   
   /*
    * This is a bit complex because devices are always created relative to
    * a particular cdevsw, including 'hidden' cdevsw's (such as the raw device
    * backing a disk subsystem overlay), so we have to compare both the
    * devsw and udev fields to locate the correct device.
    *
    * The device is created if it does not already exist.  If SI_ADHOC is not
    * set the device will be referenced (once) and SI_ADHOC will be set.
    * The caller must explicitly add additional references to the device if
    * the caller wishes to track additional references.
    */
   static
 dev_t  dev_t
 makedev(int x, int y)  hashdev(struct cdevsw *devsw, int x, int y)
 {  {
         struct specinfo *si;          struct specinfo *si;
         udev_t  udev;          udev_t  udev;
         int hash;          int hash;
         static int stashed;          static int stashed;
   
         if (x == umajor(NOUDEV) && y == uminor(NOUDEV))          udev = makeudev(x, y);
                 Debugger("makedev of NOUDEV");  
         udev = (x << 8) | y;  
         hash = udev % DEVT_HASH;          hash = udev % DEVT_HASH;
         LIST_FOREACH(si, &dev_hash[hash], si_hash) {          LIST_FOREACH(si, &dev_hash[hash], si_hash) {
                 if (si->si_udev == udev)                  if (si->si_devsw == devsw && si->si_udev == udev)
                         return (si);                          return (si);
         }          }
         if (stashed >= DEVT_STASH) {          if (stashed >= DEVT_STASH) {
                 MALLOC(si, struct specinfo *, sizeof(*si), M_DEVT,                  MALLOC(si, struct specinfo *, sizeof(*si), M_DEVT,
                     M_WAITOK|M_USE_RESERVE);                      M_WAITOK|M_USE_RESERVE|M_ZERO);
                 bzero(si, sizeof(*si));          } else if (LIST_FIRST(&dev_free_list)) {
         } else if (LIST_FIRST(&dev_free)) {                  si = LIST_FIRST(&dev_free_list);
                 si = LIST_FIRST(&dev_free);  
                 LIST_REMOVE(si, si_hash);                  LIST_REMOVE(si, si_hash);
         } else {          } else {
                 si = devt_stash + stashed++;                  si = devt_stash + stashed++;
                 si->si_flags |= SI_STASHED;                  si->si_flags |= SI_STASHED;
         }          }
           si->si_devsw = devsw;
           si->si_flags |= SI_HASHED | SI_ADHOC;
         si->si_udev = udev;          si->si_udev = udev;
           si->si_refs = 1;
         LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash);          LIST_INSERT_HEAD(&dev_hash[hash], si, si_hash);
         return (si);          si->si_port = devsw->d_port;
 }          devsw->d_clone(si);
           if (devsw != &dead_cdevsw)
 void                  ++devsw->d_refs;
 freedev(dev_t dev)          if (dev_ref_debug) {
 {                  printf("create    dev %p %s(minor=%08x) refs=%d\n", 
         int hash;                          si, devtoname(si), uminor(si->si_udev),
                           si->si_refs);
         if (!free_devt)  
                 return;  
         if (SLIST_FIRST(&dev->si_hlist))  
                 return;  
         if (dev->si_devsw || dev->si_drv1 || dev->si_drv2)  
                 return;  
         hash = dev->si_udev % DEVT_HASH;  
         LIST_REMOVE(dev, si_hash);  
         if (dev->si_flags & SI_STASHED) {  
                 bzero(dev, sizeof(*dev));  
                 LIST_INSERT_HEAD(&dev_free, dev, si_hash);  
         } else {  
                 FREE(dev, M_DEVT);  
         }          }
           return (si);
 }  }
   
   /*
    * Convert a device pointer to a device number
    */
 udev_t  udev_t
 dev2udev(dev_t x)  dev2udev(dev_t x)
 {  {
Line 161  dev2udev(dev_t x) Line 165  dev2udev(dev_t x)
         return (x->si_udev);          return (x->si_udev);
 }  }
   
   /*
    * Convert a device number to a device pointer.  The device is referenced
    * ad-hoc, meaning that the caller should call reference_dev() if it wishes
    * to keep ahold of the returned structure long term.
    *
    * The returned device is associated with the currently installed cdevsw
    * for the requested major number.  NODEV is returned if the major number
    * has not been registered.
    */
 dev_t  dev_t
 udev2dev(udev_t x, int b)  udev2dev(udev_t x, int b)
 {  {
           dev_t dev;
           struct cdevsw *devsw;
   
         if (x == NOUDEV)          if (x == NOUDEV || b != 0)
                 return (NODEV);                  return(NODEV);
         switch (b) {          devsw = cdevsw_get(umajor(x), uminor(x));
                 case 0:          if (devsw == NULL)
                         return makedev(umajor(x), uminor(x));                  return(NODEV);
                 case 1:          dev = hashdev(devsw, umajor(x), uminor(x));
                         printf("udev2dev: attempt to lookup block dev(%d)", x);          return(dev);
                         return NODEV;  
                 default:  
                         Debugger("udev2dev(...,X)");  
                         return NODEV;  
         }  
 }  }
   
 int  int
   dev_is_good(dev_t dev)
   {
           if (dev != NODEV && dev->si_devsw != &dead_cdevsw)
                   return(1);
           return(0);
   }
   
   /*
    * Various user device number extraction and conversion routines
    */
   int
 uminor(udev_t dev)  uminor(udev_t dev)
 {  {
         return(dev & 0xffff00ff);          return(dev & 0xffff00ff);
Line 197  makeudev(int x, int y) Line 218  makeudev(int x, int y)
         return ((x << 8) | y);          return ((x << 8) | y);
 }  }
   
   /*
    * Create an internal or external device.
    *
    * Device majors can be overloaded and used directly by the kernel without
    * conflict, but userland will only see the particular device major that
    * has been installed with cdevsw_add().
    *
    * This routine creates an ad-hoc entry for the device.  The caller must
    * call reference_dev() to track additional references beyond the ad-hoc
    * entry.  If an entry already exists, this function will set (or override)
    * its cred requirements and name (XXX DEVFS interface).
    */
 dev_t  dev_t
 make_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, int perms, const char *fmt, ...)  make_dev(struct cdevsw *devsw, int minor, uid_t uid, gid_t gid, 
           int perms, const char *fmt, ...)
 {  {
         dev_t   dev;          dev_t   dev;
         __va_list ap;          __va_list ap;
         int i;          int i;
   
           /*
            * compile the cdevsw and install the device
            */
         compile_devsw(devsw);          compile_devsw(devsw);
         dev = makedev(devsw->d_maj, minor);          dev = hashdev(devsw, devsw->d_maj, minor);
   
           /*
            * Set additional fields (XXX DEVFS interface goes here)
            */
         __va_start(ap, fmt);          __va_start(ap, fmt);
         i = kvprintf(fmt, NULL, dev->si_name, 32, ap);          i = kvprintf(fmt, NULL, dev->si_name, 32, ap);
         dev->si_name[i] = '\0';          dev->si_name[i] = '\0';
         __va_end(ap);          __va_end(ap);
         dev->si_devsw = devsw;  
   
         return (dev);          return (dev);
 }  }
   
 /*  /*
  * Because the device might not be immediately removed, destroy_dev   * This function is similar to make_dev() but no cred information or name
  * must clean out any potential module data references and install   * need be specified.
  * a device switch that returns an error for all future requests.   */
   dev_t
   make_adhoc_dev(struct cdevsw *devsw, int minor)
   {
           dev_t dev;
   
           dev = hashdev(devsw, devsw->d_maj, minor);
           return(dev);
   }
   
   /*
    * This function is similar to make_dev() except the new device is created
    * using an old device as a template.
    */
   dev_t
   make_sub_dev(dev_t odev, int minor)
   {
           dev_t   dev;
   
           dev = hashdev(odev->si_devsw, umajor(odev->si_udev), minor);
   
           /*
            * Copy cred requirements and name info XXX DEVFS.
            */
           if (dev->si_name[0] == 0 && odev->si_name[0])
                   bcopy(odev->si_name, dev->si_name, sizeof(dev->si_name));
           return (dev);
   }
   
   /*
    * destroy_dev() removes the adhoc association for a device and revectors
    * its devsw to &dead_cdevsw.
    *
    * This routine releases the reference count associated with the ADHOC
    * entry, plus releases the reference count held by the caller.  What this
    * means is that you should not call destroy_dev(make_dev(...)), because
    * make_dev() does not bump the reference count (beyond what it needs to
    * create the ad-hoc association).  Any procedure that intends to destroy
    * a device must have its own reference to it first.
  */   */
 void  void
 destroy_dev(dev_t dev)  destroy_dev(dev_t dev)
 {  {
         static struct cdevsw dead_cdevsw;          int hash;
   
           if (dev == NODEV)
                   return;
           if ((dev->si_flags & SI_ADHOC) == 0) {
                   release_dev(dev);
                   return;
           }
           if (dev_ref_debug) {
                   printf("destroy   dev %p %s(minor=%08x) refs=%d\n", 
                           dev, devtoname(dev), uminor(dev->si_udev),
                           dev->si_refs);
           }
           if (dev->si_refs < 2) {
                   printf("destroy_dev(): too few references on device! "
                           "%p %s(minor=%08x) refs=%d\n",
                       dev, devtoname(dev), uminor(dev->si_udev),
                       dev->si_refs);
           }
           dev->si_flags &= ~SI_ADHOC;
           if (dev->si_flags & SI_HASHED) {
                   hash = dev->si_udev % DEVT_HASH;
                   LIST_REMOVE(dev, si_hash);
                   dev->si_flags &= ~SI_HASHED;
           }
         if (dead_cdevsw.d_port == NULL)          if (dead_cdevsw.d_port == NULL)
                 compile_devsw(&dead_cdevsw);                  compile_devsw(&dead_cdevsw);
           if (dev->si_devsw && dev->si_devsw != &dead_cdevsw)
                   cdevsw_release(dev->si_devsw);
         dev->si_drv1 = 0;          dev->si_drv1 = 0;
         dev->si_drv2 = 0;          dev->si_drv2 = 0;
         dev->si_devsw = &dead_cdevsw;          dev->si_devsw = &dead_cdevsw;
         freedev(dev);          dev->si_port = dev->si_devsw->d_port;
           --dev->si_refs;         /* release adhoc association reference */
           release_dev(dev);       /* release callers reference */
   }
   
   /*
    * Destroy all ad-hoc device associations associated with a domain within a
    * device switch.
    */
   void
   destroy_all_dev(struct cdevsw *devsw, u_int mask, u_int match)
   {
           int i;
           dev_t dev;
           dev_t ndev;
   
           for (i = 0; i < DEVT_HASH; ++i) {
                   ndev = LIST_FIRST(&dev_hash[i]);
                   while ((dev = ndev) != NULL) {
                       ndev = LIST_NEXT(dev, si_hash);
                       KKASSERT(dev->si_flags & SI_ADHOC);
                       if (dev->si_devsw == devsw && 
                           (dev->si_udev & mask) == match
                       ) {
                           ++dev->si_refs;
                           destroy_dev(dev);
                       }
                   }
           }
   }
   
   /*
    * Add a reference to a device.  Callers generally add their own references
    * when they are going to store a device node in a variable for long periods
    * of time, to prevent a disassociation from free()ing the node.
    *
    * Also note that a caller that intends to call destroy_dev() must first
    * obtain a reference on the device.  The ad-hoc reference you get with
    * make_dev() and friends is NOT sufficient to be able to call destroy_dev().
    */
   dev_t
   reference_dev(dev_t dev)
   {
           if (dev != NODEV) {
                   ++dev->si_refs;
                   if (dev_ref_debug) {
                           printf("reference dev %p %s(minor=%08x) refs=%d\n", 
                               dev, devtoname(dev), uminor(dev->si_udev),
                               dev->si_refs);
                   }
           }
           return(dev);
   }
   
   /*
    * release a reference on a device.  The device will be freed when the last
    * reference has been released.
    *
    * NOTE: we must use si_udev to figure out the original (major, minor),
    * because si_devsw could already be pointing at dead_cdevsw.
    */
   void
   release_dev(dev_t dev)
   {
           if (dev == NODEV)
                   return;
           if (free_devt) {
                   KKASSERT(dev->si_refs > 0);
           } else {
                   if (dev->si_refs <= 0) {
                           printf("Warning: extra release of dev %p(%s)\n",
                               dev, devtoname(dev));
                           free_devt = 0;  /* prevent bad things from occuring */
                   }
           }
           --dev->si_refs;
           if (dev_ref_debug) {
                   printf("release   dev %p %s(minor=%08x) refs=%d\n", 
                           dev, devtoname(dev), uminor(dev->si_udev),
                           dev->si_refs);
           }
           if (dev->si_refs == 0) {
                   if (dev->si_flags & SI_ADHOC) {
                           printf("Warning: illegal final release on ADHOC"
                                   " device %p(%s), the device was never"
                                   " destroyed!\n",
                                   dev, devtoname(dev));
                   }
                   if (dev->si_flags & SI_HASHED) {
                           printf("Warning: last release on device, no call"
                                   " to destroy_dev() was made! dev %p(%s)\n",
                                   dev, devtoname(dev));
                           dev->si_refs = 3;
                           destroy_dev(dev);
                           dev->si_refs = 0;
                   }
                   if (SLIST_FIRST(&dev->si_hlist) != NULL) {
                           printf("Warning: last release on device, vnode"
                                   " associations still exist! dev %p(%s)\n",
                                   dev, devtoname(dev));
                           free_devt = 0;  /* prevent bad things from occuring */
                   }
                   if (dev->si_devsw && dev->si_devsw != &dead_cdevsw) {
                           cdevsw_release(dev->si_devsw);
                           dev->si_devsw = NULL;
                   }
                   if (free_devt) {
                           if (dev->si_flags & SI_STASHED) {
                                   bzero(dev, sizeof(*dev));
                                   LIST_INSERT_HEAD(&dev_free_list, dev, si_hash);
                           } else {
                                   FREE(dev, M_DEVT);
                           }
                   }
           }
 }  }
   
 const char *  const char *
Line 241  devtoname(dev_t dev) Line 458  devtoname(dev_t dev)
         char *p;          char *p;
         const char *dname;          const char *dname;
   
           if (dev == NODEV)
                   return("#nodev");
         if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {          if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
                 p = dev->si_name;                  p = dev->si_name;
                 len = sizeof(dev->si_name);                  len = sizeof(dev->si_name);
Line 258  devtoname(dev_t dev) Line 477  devtoname(dev_t dev)
         }          }
         return (dev->si_name);          return (dev->si_name);
 }  }
   

Removed from v.1.7  
changed lines
  Added in v.1.8