diff --git a/Documentation/admin-guide/devices.txt b/Documentation/admin-guide/devices.txt index 6b71852dadc2..4ec843123cc3 100644 --- a/Documentation/admin-guide/devices.txt +++ b/Documentation/admin-guide/devices.txt @@ -3081,3 +3081,8 @@ 1 = /dev/osd1 Second OSD Device ... 255 = /dev/osd255 256th OSD Device + + 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. diff --git a/fs/char_dev.c b/fs/char_dev.c index fb8507f521b2..c9d18362e89d 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -59,6 +59,29 @@ void chrdev_show(struct seq_file *f, off_t offset) #endif /* CONFIG_PROC_FS */ +static int find_dynamic_major(void) +{ + int i; + struct char_device_struct *cd; + + for (i = ARRAY_SIZE(chrdevs)-1; i > CHRDEV_MAJOR_DYN_END; i--) { + if (chrdevs[i] == NULL) + return i; + } + + 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 || cd->major != i) + return i; + } + + return -EBUSY; +} + /* * Register a single major with a specified minor range. * @@ -84,22 +107,14 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, mutex_lock(&chrdevs_lock); - /* temporary */ if (major == 0) { - for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) { - if (chrdevs[i] == NULL) - break; - } - - if (i < CHRDEV_MAJOR_DYN_END) - pr_warn("CHRDEV \"%s\" major number %d goes below the dynamic allocation range\n", - name, i); - - if (i == 0) { - ret = -EBUSY; + ret = find_dynamic_major(); + if (ret < 0) { + pr_err("CHRDEV \"%s\" dynamic allocation region is full\n", + name); goto out; } - major = i; + major = ret; } cd->major = major; diff --git a/include/linux/fs.h b/include/linux/fs.h index 7b5d6816542b..1773dcf1e318 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2473,6 +2473,10 @@ static inline void bd_unlink_disk_holder(struct block_device *bdev, #define CHRDEV_MAJOR_HASH_SIZE 255 /* 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 + extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *); extern int register_chrdev_region(dev_t, unsigned, const char *); extern int __register_chrdev(unsigned int major, unsigned int baseminor,