2022年5月7日 星期六

Linux Kernel(2.1)- MAJRO NUMBER RESERVED FOR DYNAMIC ASSIGNMENT


Documentation/admin-guide/devices.txt 文檔中描述了各個major number的用途, 而這文章的重點是dynamic的範圍從234~254與384~511
 234-254 char	RESERVED FOR DYNAMIC ASSIGNMENT
		Character devices that request a dynamic allocation of major number will
		take numbers starting from 254 and downward.

 384-511 char	RESERVED FOR DYNAMIC ASSIGNMENT
		Character devices that request a dynamic allocation of major
		number will take numbers starting from 511 and downward,
		once the 234-254 range is full.

相關的代碼如下:
__register_chrdev_region()
  |-> find_dynamic_major()
    |-> 254 ~ 234 or 511 ~ 384 有空的就拿來用

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
                        const char *name)
{
  struct char_device_struct *cd;
  cd = __register_chrdev_region(0, baseminor, count, name);
  if (IS_ERR(cd))
    return PTR_ERR(cd);
  *dev = MKDEV(cd->major, cd->baseminor);
  return 0;
}

static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
                           int minorct, const char *name)
{
  struct char_device_struct *cd, *curr, *prev = NULL;
  int ret;
  int i;

  if (major >= CHRDEV_MAJOR_MAX) {
    pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)\n",
        name, major, CHRDEV_MAJOR_MAX-1);
    return ERR_PTR(-EINVAL);
  }

  if (minorct > MINORMASK + 1 - baseminor) {
    pr_err("CHRDEV \"%s\" minor range requested (%u-%u) is out of range of maximum range (%u-%u) for a single major\n",
      name, baseminor, baseminor + minorct - 1, 0, MINORMASK);
    return ERR_PTR(-EINVAL);
  }

    cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
    if (cd == NULL)
        return ERR_PTR(-ENOMEM);

    mutex_lock(&chrdevs_lock);

    if (major == 0) {
        ret = find_dynamic_major();
        if (ret < 0) {
            pr_err("CHRDEV \"%s\" dynamic allocation region is full\n",
                 name);
            goto out;
        }
        major = ret;
    }

    ret = -EBUSY;
    i = major_to_index(major);
    for (curr = chrdevs[i]; curr; prev = curr, curr = curr->next) {
        if (curr->major < major)
            continue;

        if (curr->major > major)
            break;

        if (curr->baseminor + curr->minorct <= baseminor)
            continue;

        if (curr->baseminor >= baseminor + minorct)
            break;

        goto out;
    }

    cd->major = major;
    cd->baseminor = baseminor;
    cd->minorct = minorct;
    strlcpy(cd->name, name, sizeof(cd->name));

    if (!prev) {
        cd->next = curr;
        chrdevs[i] = cd;
    } else {
        cd->next = prev->next;
        prev->next = cd;
    }

    mutex_unlock(&chrdevs_lock);
    return cd;
out:
    mutex_unlock(&chrdevs_lock);
    kfree(cd);
    return ERR_PTR(ret);
}

/* fs/char_dev.c */
#define CHRDEV_MAJOR_MAX 512
/* Marks the bottom of the first segment of free char majors */
#define CHRDEV_MAJOR_DYN_END 234
/* Marks the top and bottom of the second segment of free char majors */
#define CHRDEV_MAJOR_DYN_EXT_START 511
#define CHRDEV_MAJOR_DYN_EXT_END 384

#define CHRDEV_MAJOR_HASH_SIZE 255
static struct char_device_struct {
    struct char_device_struct *next;
    unsigned int major;
    unsigned int baseminor;
    int minorct;
    char name[64];
    struct cdev *cdev;        /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];


static int find_dynamic_major(void)
{
    int i;
    struct char_device_struct *cd;

              /* from 254 ~ 234 */
    for (i = ARRAY_SIZE(chrdevs)-1; i >= CHRDEV_MAJOR_DYN_END; i--) {
        if (chrdevs[i] == NULL)
            return i;
    }

                /* from 511 ~ 384 */
    for (i = CHRDEV_MAJOR_DYN_EXT_START;
       i >= CHRDEV_MAJOR_DYN_EXT_END; i--) {
        for (cd = chrdevs[major_to_index(i)]; cd; cd = cd->next)
            if (cd->major == i)
                break;

        if (cd == NULL)
            return i;
    }

    return -EBUSY;
}


熱門文章