aboutsummaryrefslogtreecommitdiffstats
path: root/xenolinux-2.4.21-pre4-sparse/drivers/block/genhd.c
blob: 403d52e0a1f26915c1f343749f7c6710acf052e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/*
 *  Code extracted from
 *  linux/kernel/hd.c
 *
 *  Copyright (C) 1991-1998  Linus Torvalds
 *
 *  devfs support - jj, rgooch, 980122
 *
 *  Moved partition checking code to fs/partitions* - Russell King
 *  (linux@arm.uk.linux.org)
 */

/*
 * TODO:  rip out the remaining init crap from this file  --hch
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/kernel.h>
#include <linux/blk.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/seq_file.h>


/*
 * Global kernel list of partitioning information.
 *
 * XXX: you should _never_ access this directly.
 *	the only reason this is exported is source compatiblity.
 */
/*static*/ struct gendisk *gendisk_head;
static struct gendisk *gendisk_array[MAX_BLKDEV];
static rwlock_t gendisk_lock = RW_LOCK_UNLOCKED;

EXPORT_SYMBOL(gendisk_head);


/**
 * add_gendisk - add partitioning information to kernel list
 * @gp: per-device partitioning information
 *
 * This function registers the partitioning information in @gp
 * with the kernel.
 */
void
add_gendisk(struct gendisk *gp)
{
	struct gendisk *sgp;

	write_lock(&gendisk_lock);

	/*
 	 *	In 2.5 this will go away. Fix the drivers who rely on
 	 *	old behaviour.
 	 */

	for (sgp = gendisk_head; sgp; sgp = sgp->next)
	{
		if (sgp == gp)
		{
//			printk(KERN_ERR "add_gendisk: device major %d is buggy and added a live gendisk!\n",
//				sgp->major)
			goto out;
		}
	}
	gendisk_array[gp->major] = gp;
	gp->next = gendisk_head;
	gendisk_head = gp;
out:
	write_unlock(&gendisk_lock);
}

EXPORT_SYMBOL(add_gendisk);


/**
 * del_gendisk - remove partitioning information from kernel list
 * @gp: per-device partitioning information
 *
 * This function unregisters the partitioning information in @gp
 * with the kernel.
 */
void
del_gendisk(struct gendisk *gp)
{
	struct gendisk **gpp;

	write_lock(&gendisk_lock);
	gendisk_array[gp->major] = NULL;
	for (gpp = &gendisk_head; *gpp; gpp = &((*gpp)->next))
		if (*gpp == gp)
			break;
	if (*gpp)
		*gpp = (*gpp)->next;
	write_unlock(&gendisk_lock);
}

EXPORT_SYMBOL(del_gendisk);


/**
 * get_gendisk - get partitioning information for a given device
 * @dev: device to get partitioning information for
 *
 * This function gets the structure containing partitioning
 * information for the given device @dev.
 */
struct gendisk *
get_gendisk(kdev_t dev)
{
	struct gendisk *gp = NULL;
	int maj = MAJOR(dev);

	read_lock(&gendisk_lock);
	if ((gp = gendisk_array[maj]))
		goto out;

	/* This is needed for early 2.4 source compatiblity.  --hch */
	for (gp = gendisk_head; gp; gp = gp->next)
		if (gp->major == maj)
			break;
out:
	read_unlock(&gendisk_lock);
	return gp;
}

EXPORT_SYMBOL(get_gendisk);


/**
 * walk_gendisk - issue a command for every registered gendisk
 * @walk: user-specified callback
 * @data: opaque data for the callback
 *
 * This function walks through the gendisk chain and calls back
 * into @walk for every element.
 */
int
walk_gendisk(int (*walk)(struct gendisk *, void *), void *data)
{
	struct gendisk *gp;
	int error = 0;

	read_lock(&gendisk_lock);
	for (gp = gendisk_head; gp; gp = gp->next)
		if ((error = walk(gp, data)))
			break;
	read_unlock(&gendisk_lock);

	return error;
}

#ifdef CONFIG_PROC_FS
/* iterator */
static void *part_start(struct seq_file *s, loff_t *ppos)
{
	struct gendisk *gp;
	loff_t pos = *ppos;

	read_lock(&gendisk_lock);
	for (gp = gendisk_head; gp; gp = gp->next)
		if (!pos--)
			return gp;
	return NULL;
}

static void *part_next(struct seq_file *s, void *v, loff_t *pos)
{
	++*pos;
	return ((struct gendisk *)v)->next;
}

static void part_stop(struct seq_file *s, void *v)
{
	read_unlock(&gendisk_lock);
}

static int part_show(struct seq_file *s, void *v)
{
	struct gendisk *gp = v;
	char buf[64];
	int n;

	if (gp == gendisk_head) {
		seq_puts(s, "major minor  #blocks  start_sect   nr_sects name"
#ifdef CONFIG_BLK_STATS
			    "     rio rmerge rsect ruse wio wmerge "
			    "wsect wuse running use aveq"
#endif
			   "\n\n");
	}

	/* show the full disk and all non-0 size partitions of it */
	for (n = 0; n < (gp->nr_real << gp->minor_shift); n++) {
		if (gp->part[n].nr_sects) {
#ifdef CONFIG_BLK_STATS
			struct hd_struct *hd = &gp->part[n];

			disk_round_stats(hd);
			seq_printf(s, "%4d  %4d %10d %10ld %s "
				      "%d %d %d %d %d %d %d %d %d %d %d\n",
				      gp->major, n, gp->sizes[n],
				      gp->part[n].start_sect,
				      gp->part[n].nr_sects,
				      disk_name(gp, n, buf),
				      hd->rd_ios, hd->rd_merges,
#define MSEC(x) ((x) * 1000 / HZ)
				      hd->rd_sectors, MSEC(hd->rd_ticks),
				      hd->wr_ios, hd->wr_merges,
				      hd->wr_sectors, MSEC(hd->wr_ticks),
				      hd->ios_in_flight, MSEC(hd->io_ticks),
				      MSEC(hd->aveq));
#else
			seq_printf(s, "%4d  %4d %10d %10ld %10ld %s\n",
				   gp->major, n, gp->sizes[n],
				   gp->part[n].start_sect,
				   gp->part[n].nr_sects,
				   disk_name(gp, n, buf));
#endif /* CONFIG_BLK_STATS */
		}
	}

	return 0;
}

struct seq_operations partitions_op = {
	.start		= part_start,
	.next		= part_next,
	.stop		= part_stop,
	.show		= part_show,
};
#endif

extern int blk_dev_init(void);
extern int net_dev_init(void);
extern void console_map_init(void);
extern int atmdev_init(void);

int __init device_init(void)
{
	blk_dev_init();
	sti();
#ifdef CONFIG_NET
	net_dev_init();
#endif
#ifdef CONFIG_ATM
	(void) atmdev_init();
#endif
#ifdef CONFIG_VT
	console_map_init();
#endif
	return 0;
}

__initcall(device_init);