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
add_gendisk(struct gendisk * gp)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
del_gendisk(struct gendisk * gp)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 *
get_gendisk(kdev_t dev)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
walk_gendisk(int (* walk)(struct gendisk *,void *),void * data)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 */
part_start(struct seq_file * s,loff_t * ppos)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 
part_next(struct seq_file * s,void * v,loff_t * pos)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 
part_stop(struct seq_file * s,void * v)180 static void part_stop(struct seq_file *s, void *v)
181 {
182 	read_unlock_irq(&gendisk_lock);
183 }
184 
part_show(struct seq_file * s,void * v)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 
device_init(void)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);
259