[PATCH] NFSv4: Clean up nfs4 lock state accounting
[linux-2.6] / fs / partitions / devfs.c
1 /*
2  * This tries to keep block devices away from devfs as much as possible.
3  */
4 #include <linux/fs.h>
5 #include <linux/devfs_fs_kernel.h>
6 #include <linux/vmalloc.h>
7 #include <linux/genhd.h>
8 #include <linux/bitops.h>
9 #include <asm/semaphore.h>
10
11
12 struct unique_numspace {
13         u32               num_free;          /*  Num free in bits       */
14         u32               length;            /*  Array length in bytes  */
15         unsigned long     *bits;
16         struct semaphore  mutex;
17 };
18
19 static DECLARE_MUTEX(numspace_mutex);
20
21 static int expand_numspace(struct unique_numspace *s)
22 {
23         u32 length;
24         void *bits;
25
26         if (s->length < 16)
27                 length = 16;
28         else
29                 length = s->length << 1;
30
31         bits = vmalloc(length);
32         if (!bits)
33                 return -ENOMEM;
34         if (s->bits) {
35                 memcpy(bits, s->bits, s->length);
36                 vfree(s->bits);
37         }
38                 
39         s->num_free = (length - s->length) << 3;
40         s->bits = bits;
41         memset(bits + s->length, 0, length - s->length);
42         s->length = length;
43
44         return 0;
45 }
46
47 static int alloc_unique_number(struct unique_numspace *s)
48 {
49         int rval = 0;
50
51         down(&numspace_mutex);
52         if (s->num_free < 1)
53                 rval = expand_numspace(s);
54         if (!rval) {
55                 rval = find_first_zero_bit(s->bits, s->length << 3);
56                 --s->num_free;
57                 __set_bit(rval, s->bits);
58         }
59         up(&numspace_mutex);
60
61         return rval;
62 }
63
64 static void dealloc_unique_number(struct unique_numspace *s, int number)
65 {
66         int old_val;
67
68         if (number >= 0) {
69                 down(&numspace_mutex);
70                 old_val = __test_and_clear_bit(number, s->bits);
71                 if (old_val)
72                         ++s->num_free;
73                 up(&numspace_mutex);
74         }
75 }
76
77 static struct unique_numspace disc_numspace;
78 static struct unique_numspace cdrom_numspace;
79
80 void devfs_add_partitioned(struct gendisk *disk)
81 {
82         char dirname[64], symlink[16];
83
84         devfs_mk_dir(disk->devfs_name);
85         devfs_mk_bdev(MKDEV(disk->major, disk->first_minor),
86                         S_IFBLK|S_IRUSR|S_IWUSR,
87                         "%s/disc", disk->devfs_name);
88
89         disk->number = alloc_unique_number(&disc_numspace);
90
91         sprintf(symlink, "discs/disc%d", disk->number);
92         sprintf(dirname, "../%s", disk->devfs_name);
93         devfs_mk_symlink(symlink, dirname);
94
95 }
96
97 void devfs_add_disk(struct gendisk *disk)
98 {
99         devfs_mk_bdev(MKDEV(disk->major, disk->first_minor),
100                         (disk->flags & GENHD_FL_CD) ?
101                                 S_IFBLK|S_IRUGO|S_IWUGO :
102                                 S_IFBLK|S_IRUSR|S_IWUSR,
103                         "%s", disk->devfs_name);
104
105         if (disk->flags & GENHD_FL_CD) {
106                 char dirname[64], symlink[16];
107
108                 disk->number = alloc_unique_number(&cdrom_numspace);
109
110                 sprintf(symlink, "cdroms/cdrom%d", disk->number);
111                 sprintf(dirname, "../%s", disk->devfs_name);
112                 devfs_mk_symlink(symlink, dirname);
113         }
114 }
115
116 void devfs_remove_disk(struct gendisk *disk)
117 {
118         if (disk->minors != 1) {
119                 devfs_remove("discs/disc%d", disk->number);
120                 dealloc_unique_number(&disc_numspace, disk->number);
121                 devfs_remove("%s/disc", disk->devfs_name);
122         }
123         if (disk->flags & GENHD_FL_CD) {
124                 devfs_remove("cdroms/cdrom%d", disk->number);
125                 dealloc_unique_number(&cdrom_numspace, disk->number);
126         }
127         devfs_remove(disk->devfs_name);
128 }
129
130