added a lot of printk output to ease writing of emulator
[linux-2.4.21-pre4.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
53         write_lock(&gendisk_lock);
54
55         /*
56          *      In 2.5 this will go away. Fix the drivers who rely on
57          *      old behaviour.
58          */
59
60         for (sgp = gendisk_head; sgp; sgp = sgp->next)
61         {
62                 if (sgp == gp)
63                 {
64 //                      printk(KERN_ERR "add_gendisk: device major %d is buggy and added a live gendisk!\n",
65 //                              sgp->major)
66                         goto out;
67                 }
68         }
69         gendisk_array[gp->major] = gp;
70         gp->next = gendisk_head;
71         gendisk_head = gp;
72 out:
73         write_unlock(&gendisk_lock);
74 }
75
76 EXPORT_SYMBOL(add_gendisk);
77
78
79 /**
80  * del_gendisk - remove partitioning information from kernel list
81  * @gp: per-device partitioning information
82  *
83  * This function unregisters the partitioning information in @gp
84  * with the kernel.
85  */
86 void
87 del_gendisk(struct gendisk *gp)
88 {
89         struct gendisk **gpp;
90
91         write_lock(&gendisk_lock);
92         gendisk_array[gp->major] = NULL;
93         for (gpp = &gendisk_head; *gpp; gpp = &((*gpp)->next))
94                 if (*gpp == gp)
95                         break;
96         if (*gpp)
97                 *gpp = (*gpp)->next;
98         write_unlock(&gendisk_lock);
99 }
100
101 EXPORT_SYMBOL(del_gendisk);
102
103
104 /**
105  * get_gendisk - get partitioning information for a given device
106  * @dev: device to get partitioning information for
107  *
108  * This function gets the structure containing partitioning
109  * information for the given device @dev.
110  */
111 struct gendisk *
112 get_gendisk(kdev_t dev)
113 {
114         struct gendisk *gp = NULL;
115         int maj = MAJOR(dev);
116
117         read_lock(&gendisk_lock);
118         if ((gp = gendisk_array[maj]))
119                 goto out;
120
121         /* This is needed for early 2.4 source compatiblity.  --hch */
122         for (gp = gendisk_head; gp; gp = gp->next)
123                 if (gp->major == maj)
124                         break;
125 out:
126         read_unlock(&gendisk_lock);
127         return gp;
128 }
129
130 EXPORT_SYMBOL(get_gendisk);
131
132
133 /**
134  * walk_gendisk - issue a command for every registered gendisk
135  * @walk: user-specified callback
136  * @data: opaque data for the callback
137  *
138  * This function walks through the gendisk chain and calls back
139  * into @walk for every element.
140  */
141 int
142 walk_gendisk(int (*walk)(struct gendisk *, void *), void *data)
143 {
144         struct gendisk *gp;
145         int error = 0;
146
147         read_lock(&gendisk_lock);
148         for (gp = gendisk_head; gp; gp = gp->next)
149                 if ((error = walk(gp, data)))
150                         break;
151         read_unlock(&gendisk_lock);
152
153         return error;
154 }
155
156 #ifdef CONFIG_PROC_FS
157 /* iterator */
158 static void *part_start(struct seq_file *s, loff_t *ppos)
159 {
160         struct gendisk *gp;
161         loff_t pos = *ppos;
162
163         read_lock(&gendisk_lock);
164         for (gp = gendisk_head; gp; gp = gp->next)
165                 if (!pos--)
166                         return gp;
167         return NULL;
168 }
169
170 static void *part_next(struct seq_file *s, void *v, loff_t *pos)
171 {
172         ++*pos;
173         return ((struct gendisk *)v)->next;
174 }
175
176 static void part_stop(struct seq_file *s, void *v)
177 {
178         read_unlock(&gendisk_lock);
179 }
180
181 static int part_show(struct seq_file *s, void *v)
182 {
183         struct gendisk *gp = v;
184         char buf[64];
185         int n;
186
187         if (gp == gendisk_head) {
188                 seq_puts(s, "major minor  #blocks  name"
189 #ifdef CONFIG_BLK_STATS
190                             "     rio rmerge rsect ruse wio wmerge "
191                             "wsect wuse running use aveq"
192 #endif
193                            "\n\n");
194         }
195
196         /* show the full disk and all non-0 size partitions of it */
197         for (n = 0; n < (gp->nr_real << gp->minor_shift); n++) {
198                 if (gp->part[n].nr_sects) {
199 #ifdef CONFIG_BLK_STATS
200                         struct hd_struct *hd = &gp->part[n];
201
202                         disk_round_stats(hd);
203                         seq_printf(s, "%4d  %4d %10d %s "
204                                       "%d %d %d %d %d %d %d %d %d %d %d\n",
205                                       gp->major, n, gp->sizes[n],
206                                       disk_name(gp, n, buf),
207                                       hd->rd_ios, hd->rd_merges,
208 #define MSEC(x) ((x) * 1000 / HZ)
209                                       hd->rd_sectors, MSEC(hd->rd_ticks),
210                                       hd->wr_ios, hd->wr_merges,
211                                       hd->wr_sectors, MSEC(hd->wr_ticks),
212                                       hd->ios_in_flight, MSEC(hd->io_ticks),
213                                       MSEC(hd->aveq));
214 #else
215                         seq_printf(s, "%4d  %4d %10d %s\n",
216                                    gp->major, n, gp->sizes[n],
217                                    disk_name(gp, n, buf));
218 #endif /* CONFIG_BLK_STATS */
219                 }
220         }
221
222         return 0;
223 }
224
225 struct seq_operations partitions_op = {
226         .start          = part_start,
227         .next           = part_next,
228         .stop           = part_stop,
229         .show           = part_show,
230 };
231 #endif
232
233 extern int blk_dev_init(void);
234 extern int net_dev_init(void);
235 extern void console_map_init(void);
236 extern int atmdev_init(void);
237
238 int __init device_init(void)
239 {
240         blk_dev_init();
241         sti();
242 #ifdef CONFIG_NET
243         net_dev_init();
244 #endif
245 #ifdef CONFIG_ATM
246         (void) atmdev_init();
247 #endif
248 #ifdef CONFIG_VT
249         console_map_init();
250 #endif
251 #ifdef CONFIG_VIODASD
252         viodasd_init();
253 #endif
254         return 0;
255 }
256
257 __initcall(device_init);