char_dev: extend dynamic allocation of majors into a higher range
[linux-2.6-block.git] / fs / char_dev.c
index fb8507f521b265ba8feba385154cc908eb812f44..c9d18362e89de0a060f96ae8e41cc05543c9dc6f 100644 (file)
@@ -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;