include upstream ip1000a driver version 2.09f
[linux-2.4.git] / drivers / block / genhd.c
1 /*
2  *  Code extracted from
3  *  linux/kernel/hd.c
4  *
5  *  Copyright (C) 1991-1998  Linus Torvalds
6  *
7  *  devfs support - jj, rgooch, 980122
8  *
9  *  Moved partition checking code to fs/partitions* - Russell King
10  *  (linux@arm.uk.linux.org)
11  */
12
13 /*
14  * TODO:  rip out the remaining init crap from this file  --hch
15  */
16
17 #include <linux/config.h>
18 #include <linux/module.h>
19 #include <linux/fs.h>
20 #include <linux/genhd.h>
21 #include <linux/kernel.h>
22 #include <linux/blk.h>
23 #include <linux/init.h>
24 #include <linux/spinlock.h>
25 #include <linux/seq_file.h>
26
27
28 /*
29  * Global kernel list of partitioning information.
30  *
31  * XXX: you should _never_ access this directly.
32  *      the only reason this is exported is source compatiblity.
33  */
34 /*static*/ struct gendisk *gendisk_head;
35 static struct gendisk *gendisk_array[MAX_BLKDEV];
36 static rwlock_t gendisk_lock = RW_LOCK_UNLOCKED;
37
38 EXPORT_SYMBOL(gendisk_head);
39
40
41 /**
42  * add_gendisk - add partitioning information to kernel list
43  * @gp: per-device partitioning information
44  *
45  * This function registers the partitioning information in @gp
46  * with the kernel.
47  */
48 void
49 add_gendisk(struct gendisk *gp)
50 {
51         struct gendisk *sgp;
52         unsigned long flags;
53
54         write_lock_irqsave(&gendisk_lock, flags);
55
56         /*
57          *      In 2.5 this will go away. Fix the drivers who rely on
58          *      old behaviour.
59          */
60
61         for (sgp = gendisk_head; sgp; sgp = sgp->next)
62         {
63                 if (sgp == gp)
64                 {
65 //                      printk(KERN_ERR "add_gendisk: device major %d is buggy and added a live gendisk!\n",
66 //                              sgp->major)
67                         goto out;
68                 }
69         }
70         gendisk_array[gp->major] = gp;
71         gp->next = gendisk_head;
72         gendisk_head = gp;
73 out:
74         write_unlock_irqrestore(&gendisk_lock, flags);
75 }
76
77 EXPORT_SYMBOL(add_gendisk);
78
79
80 /**
81  * del_gendisk - remove partitioning information from kernel list
82  * @gp: per-device partitioning information
83  *
84  * This function unregisters the partitioning information in @gp
85  * with the kernel.
86  */
87 void
88 del_gendisk(struct gendisk *gp)
89 {
90         struct gendisk **gpp;
91         unsigned long flags;
92
93         write_lock_irqsave(&gendisk_lock, flags);
94         gendisk_array[gp->major] = NULL;
95         for (gpp = &gendisk_head; *gpp; gpp = &((*gpp)->next))
96                 if (*gpp == gp)
97                         break;
98         if (*gpp)
99                 *gpp = (*gpp)->next;
100         write_unlock_irqrestore(&gendisk_lock, flags);
101 }
102
103 EXPORT_SYMBOL(del_gendisk);
104
105
106 /**
107  * get_gendisk - get partitioning information for a given device
108  * @dev: device to get partitioning information for
109  *
110  * This function gets the structure containing partitioning
111  * information for the given device @dev.
112  */
113 struct gendisk *
114 get_gendisk(kdev_t dev)
115 {
116         struct gendisk *gp = NULL;
117         int maj = MAJOR(dev);
118         unsigned long flags;
119
120         read_lock_irqsave(&gendisk_lock, flags);
121         if ((gp = gendisk_array[maj]))
122                 goto out;
123
124         /* This is needed for early 2.4 source compatiblity.  --hch */
125         for (gp = gendisk_head; gp; gp = gp->next)
126                 if (gp->major == maj)
127                         break;
128 out:
129         read_unlock_irqrestore(&gendisk_lock, flags);
130         return gp;
131 }
132
133 EXPORT_SYMBOL(get_gendisk);
134
135
136 /**
137  * walk_gendisk - issue a command for every registered gendisk
138  * @walk: user-specified callback
139  * @data: opaque data for the callback
140  *
141  * This function walks through the gendisk chain and calls back
142  * into @walk for every element.
143  */
144 int
145 walk_gendisk(int (*walk)(struct gendisk *, void *), void *data)
146 {
147         struct gendisk *gp;
148         int error = 0;
149         unsigned long flags;
150
151         read_lock_irqsave(&gendisk_lock, flags);
152         for (gp = gendisk_head; gp; gp = gp->next)
153                 if ((error = walk(gp, data)))
154                         break;
155         read_unlock_irqrestore(&gendisk_lock, flags);
156
157         return error;
158 }
159
160 #ifdef CONFIG_PROC_FS
161 /* iterator */
162 static void *part_start(struct seq_file *s, loff_t *ppos)
163 {
164         struct gendisk *gp;
165         loff_t pos = *ppos;
166
167         read_lock_irq(&gendisk_lock);
168         for (gp = gendisk_head; gp; gp = gp->next)
169                 if (!pos--)
170                         return gp;
171         return NULL;
172 }
173
174 static void *part_next(struct seq_file *s, void *v, loff_t *pos)
175 {
176         ++*pos;
177         return ((struct gendisk *)v)->next;
178 }
179
180 static void part_stop(struct seq_file *s, void *v)
181 {
182         read_unlock_irq(&gendisk_lock);
183 }
184
185 static int part_show(struct seq_file *s, void *v)
186 {
187         struct gendisk *gp = v;
188         char buf[64];
189         int n;
190
191         if (gp == gendisk_head) {
192                 seq_puts(s, "major minor  #blocks  name"
193 #ifdef CONFIG_BLK_STATS
194                             "     rio rmerge rsect ruse wio wmerge "
195                             "wsect wuse running use aveq"
196 #endif
197                            "\n\n");
198         }
199
200         /* show the full disk and all non-0 size partitions of it */
201         for (n = 0; n < (gp->nr_real << gp->minor_shift); n++) {
202                 if (gp->part[n].nr_sects) {
203 #ifdef CONFIG_BLK_STATS
204                         struct hd_struct *hd = &gp->part[n];
205
206                         disk_round_stats(hd);
207                         seq_printf(s, "%4d  %4d %10d %s "
208                                       "%u %u %u %u %u %u %u %u %d %u %u\n",
209                                       gp->major, n, gp->sizes[n],
210                                       disk_name(gp, n, buf),
211                                       hd->rd_ios, hd->rd_merges,
212 #define MSEC(x) ((x) * 1000 / HZ)
213                                       hd->rd_sectors, MSEC(hd->rd_ticks),
214                                       hd->wr_ios, hd->wr_merges,
215                                       hd->wr_sectors, MSEC(hd->wr_ticks),
216                                       hd->ios_in_flight, MSEC(hd->io_ticks),
217                                       MSEC(hd->aveq));
218 #else
219                         seq_printf(s, "%4d  %4d %10d %s\n",
220                                    gp->major, n, gp->sizes[n],
221                                    disk_name(gp, n, buf));
222 #endif /* CONFIG_BLK_STATS */
223                 }
224         }
225
226         return 0;
227 }
228
229 struct seq_operations partitions_op = {
230         .start          = part_start,
231         .next           = part_next,
232         .stop           = part_stop,
233         .show           = part_show,
234 };
235 #endif
236
237 extern int blk_dev_init(void);
238 extern int net_dev_init(void);
239 extern void console_map_init(void);
240 extern int atmdev_init(void);
241
242 int __init device_init(void)
243 {
244         blk_dev_init();
245         sti();
246 #ifdef CONFIG_NET
247         net_dev_init();
248 #endif
249 #ifdef CONFIG_ATM
250         (void) atmdev_init();
251 #endif
252 #ifdef CONFIG_VT
253         console_map_init();
254 #endif
255         return 0;
256 }
257
258 __initcall(device_init);