Merge tag 'secureexec-v4.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[platform/kernel/linux-starfive.git] / fs / char_dev.c
index fb8507f..ebcc8fb 100644 (file)
@@ -28,6 +28,8 @@ static struct kobj_map *cdev_map;
 
 static DEFINE_MUTEX(chrdevs_lock);
 
+#define CHRDEV_MAJOR_HASH_SIZE 255
+
 static struct char_device_struct {
        struct char_device_struct *next;
        unsigned int major;
@@ -49,16 +51,39 @@ void chrdev_show(struct seq_file *f, off_t offset)
 {
        struct char_device_struct *cd;
 
-       if (offset < CHRDEV_MAJOR_HASH_SIZE) {
-               mutex_lock(&chrdevs_lock);
-               for (cd = chrdevs[offset]; cd; cd = cd->next)
+       mutex_lock(&chrdevs_lock);
+       for (cd = chrdevs[major_to_index(offset)]; cd; cd = cd->next) {
+               if (cd->major == offset)
                        seq_printf(f, "%3d %s\n", cd->major, cd->name);
-               mutex_unlock(&chrdevs_lock);
        }
+       mutex_unlock(&chrdevs_lock);
 }
 
 #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 +109,21 @@ __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;
+       }
+
+       if (major >= CHRDEV_MAJOR_MAX) {
+               pr_err("CHRDEV \"%s\" major requested (%d) is greater than the maximum (%d)\n",
+                      name, major, CHRDEV_MAJOR_MAX);
+               ret = -EINVAL;
+               goto out;
        }
 
        cd->major = major;