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