Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

char_dev: order /proc/devices by major number

Presently, the order of the char devices listed in /proc/devices is not
entirely sequential. If a char device has a major number greater than
CHRDEV_MAJOR_HASH_SIZE (255), it will be ordered as if its major were
module 255. For example, 511 appears after 1.

This patch cleans that up and prints each major number in the correct
order, regardless of where they are stored in the hash table.

In order to do this, we introduce CHRDEV_MAJOR_MAX as an artificial
limit (chosen to be 511). It will then print all devices in major
order number from 0 to the maximum.

Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Logan Gunthorpe and committed by
Greg Kroah-Hartman
8a932f73 a5d31a3f

+18 -9
+13 -4
fs/char_dev.c
··· 28 28 29 29 static DEFINE_MUTEX(chrdevs_lock); 30 30 31 + #define CHRDEV_MAJOR_HASH_SIZE 255 32 + 31 33 static struct char_device_struct { 32 34 struct char_device_struct *next; 33 35 unsigned int major; ··· 51 49 { 52 50 struct char_device_struct *cd; 53 51 54 - if (offset < CHRDEV_MAJOR_HASH_SIZE) { 55 - mutex_lock(&chrdevs_lock); 56 - for (cd = chrdevs[offset]; cd; cd = cd->next) 52 + mutex_lock(&chrdevs_lock); 53 + for (cd = chrdevs[major_to_index(offset)]; cd; cd = cd->next) { 54 + if (cd->major == offset) 57 55 seq_printf(f, "%3d %s\n", cd->major, cd->name); 58 - mutex_unlock(&chrdevs_lock); 59 56 } 57 + mutex_unlock(&chrdevs_lock); 60 58 } 61 59 62 60 #endif /* CONFIG_PROC_FS */ ··· 117 115 goto out; 118 116 } 119 117 major = ret; 118 + } 119 + 120 + if (major >= CHRDEV_MAJOR_MAX) { 121 + pr_err("CHRDEV \"%s\" major requested (%d) is greater than the maximum (%d)\n", 122 + name, major, CHRDEV_MAJOR_MAX); 123 + ret = -EINVAL; 124 + goto out; 120 125 } 121 126 122 127 cd->major = major;
+4 -4
fs/proc/devices.c
··· 7 7 { 8 8 int i = *(loff_t *) v; 9 9 10 - if (i < CHRDEV_MAJOR_HASH_SIZE) { 10 + if (i < CHRDEV_MAJOR_MAX) { 11 11 if (i == 0) 12 12 seq_puts(f, "Character devices:\n"); 13 13 chrdev_show(f, i); 14 14 } 15 15 #ifdef CONFIG_BLOCK 16 16 else { 17 - i -= CHRDEV_MAJOR_HASH_SIZE; 17 + i -= CHRDEV_MAJOR_MAX; 18 18 if (i == 0) 19 19 seq_puts(f, "\nBlock devices:\n"); 20 20 blkdev_show(f, i); ··· 25 25 26 26 static void *devinfo_start(struct seq_file *f, loff_t *pos) 27 27 { 28 - if (*pos < (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE)) 28 + if (*pos < (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_MAX)) 29 29 return pos; 30 30 return NULL; 31 31 } ··· 33 33 static void *devinfo_next(struct seq_file *f, void *v, loff_t *pos) 34 34 { 35 35 (*pos)++; 36 - if (*pos >= (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_HASH_SIZE)) 36 + if (*pos >= (BLKDEV_MAJOR_HASH_SIZE + CHRDEV_MAJOR_MAX)) 37 37 return NULL; 38 38 return pos; 39 39 }
+1 -1
include/linux/fs.h
··· 2470 2470 #endif 2471 2471 2472 2472 /* fs/char_dev.c */ 2473 - #define CHRDEV_MAJOR_HASH_SIZE 255 2473 + #define CHRDEV_MAJOR_MAX 512 2474 2474 /* Marks the bottom of the first segment of free char majors */ 2475 2475 #define CHRDEV_MAJOR_DYN_END 234 2476 2476 /* Marks the top and bottom of the second segment of free char majors */