genhd.c 18.3 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
/*
 *  gendisk handling
 */

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/genhd.h>
8
#include <linux/kdev_t.h>
Linus Torvalds's avatar
Linus Torvalds committed
9
10
11
12
13
14
15
16
#include <linux/kernel.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/kmod.h>
#include <linux/kobj_map.h>
17
#include <linux/buffer_head.h>
18
#include <linux/mutex.h>
Linus Torvalds's avatar
Linus Torvalds committed
19

20
21
#include "blk.h"

22
23
24
25
static DEFINE_MUTEX(block_class_lock);
#ifndef CONFIG_SYSFS_DEPRECATED
struct kobject *block_depr;
#endif
Linus Torvalds's avatar
Linus Torvalds committed
26

Adrian Bunk's avatar
Adrian Bunk committed
27
28
static struct device_type disk_type;

Linus Torvalds's avatar
Linus Torvalds committed
29
30
31
32
33
34
35
36
/*
 * Can be deleted altogether. Later.
 *
 */
static struct blk_major_name {
	struct blk_major_name *next;
	int major;
	char name[16];
37
} *major_names[BLKDEV_MAJOR_HASH_SIZE];
Linus Torvalds's avatar
Linus Torvalds committed
38
39
40
41

/* index in the above - for now: assume no multimajor ranges */
static inline int major_to_index(int major)
{
42
	return major % BLKDEV_MAJOR_HASH_SIZE;
43
44
}

45
46
#ifdef CONFIG_PROC_FS
void blkdev_show(struct seq_file *f, off_t offset)
47
{
48
	struct blk_major_name *dp;
49

50
	if (offset < BLKDEV_MAJOR_HASH_SIZE) {
51
		mutex_lock(&block_class_lock);
52
53
		for (dp = major_names[offset]; dp; dp = dp->next)
			seq_printf(f, "%3d %s\n", dp->major, dp->name);
54
		mutex_unlock(&block_class_lock);
Linus Torvalds's avatar
Linus Torvalds committed
55
56
	}
}
57
#endif /* CONFIG_PROC_FS */
Linus Torvalds's avatar
Linus Torvalds committed
58
59
60
61
62
63

int register_blkdev(unsigned int major, const char *name)
{
	struct blk_major_name **n, *p;
	int index, ret = 0;

64
	mutex_lock(&block_class_lock);
Linus Torvalds's avatar
Linus Torvalds committed
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

	/* temporary */
	if (major == 0) {
		for (index = ARRAY_SIZE(major_names)-1; index > 0; index--) {
			if (major_names[index] == NULL)
				break;
		}

		if (index == 0) {
			printk("register_blkdev: failed to get major for %s\n",
			       name);
			ret = -EBUSY;
			goto out;
		}
		major = index;
		ret = major;
	}

	p = kmalloc(sizeof(struct blk_major_name), GFP_KERNEL);
	if (p == NULL) {
		ret = -ENOMEM;
		goto out;
	}

	p->major = major;
	strlcpy(p->name, name, sizeof(p->name));
	p->next = NULL;
	index = major_to_index(major);

	for (n = &major_names[index]; *n; n = &(*n)->next) {
		if ((*n)->major == major)
			break;
	}
	if (!*n)
		*n = p;
	else
		ret = -EBUSY;

	if (ret < 0) {
		printk("register_blkdev: cannot get major %d for %s\n",
		       major, name);
		kfree(p);
	}
out:
109
	mutex_unlock(&block_class_lock);
Linus Torvalds's avatar
Linus Torvalds committed
110
111
112
113
114
	return ret;
}

EXPORT_SYMBOL(register_blkdev);

115
void unregister_blkdev(unsigned int major, const char *name)
Linus Torvalds's avatar
Linus Torvalds committed
116
117
118
119
120
{
	struct blk_major_name **n;
	struct blk_major_name *p = NULL;
	int index = major_to_index(major);

121
	mutex_lock(&block_class_lock);
Linus Torvalds's avatar
Linus Torvalds committed
122
123
124
	for (n = &major_names[index]; *n; n = &(*n)->next)
		if ((*n)->major == major)
			break;
125
126
127
	if (!*n || strcmp((*n)->name, name)) {
		WARN_ON(1);
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
128
129
130
		p = *n;
		*n = p->next;
	}
131
	mutex_unlock(&block_class_lock);
Linus Torvalds's avatar
Linus Torvalds committed
132
133
134
135
136
137
138
139
140
141
142
143
	kfree(p);
}

EXPORT_SYMBOL(unregister_blkdev);

static struct kobj_map *bdev_map;

/*
 * Register device numbers dev..(dev+range-1)
 * range must be nonzero
 * The hash chain is sorted on range, so that subranges can override.
 */
144
void blk_register_region(dev_t devt, unsigned long range, struct module *module,
Linus Torvalds's avatar
Linus Torvalds committed
145
146
147
			 struct kobject *(*probe)(dev_t, int *, void *),
			 int (*lock)(dev_t, void *), void *data)
{
148
	kobj_map(bdev_map, devt, range, module, probe, lock, data);
Linus Torvalds's avatar
Linus Torvalds committed
149
150
151
152
}

EXPORT_SYMBOL(blk_register_region);

153
void blk_unregister_region(dev_t devt, unsigned long range)
Linus Torvalds's avatar
Linus Torvalds committed
154
{
155
	kobj_unmap(bdev_map, devt, range);
Linus Torvalds's avatar
Linus Torvalds committed
156
157
158
159
}

EXPORT_SYMBOL(blk_unregister_region);

160
static struct kobject *exact_match(dev_t devt, int *part, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
161
162
{
	struct gendisk *p = data;
163
164

	return &p->dev.kobj;
Linus Torvalds's avatar
Linus Torvalds committed
165
166
}

167
static int exact_lock(dev_t devt, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
{
	struct gendisk *p = data;

	if (!get_disk(p))
		return -1;
	return 0;
}

/**
 * add_disk - add partitioning information to kernel list
 * @disk: per-device partitioning information
 *
 * This function registers the partitioning information in @disk
 * with the kernel.
 */
void add_disk(struct gendisk *disk)
{
185
186
	struct backing_dev_info *bdi;

Linus Torvalds's avatar
Linus Torvalds committed
187
188
189
190
191
	disk->flags |= GENHD_FL_UP;
	blk_register_region(MKDEV(disk->major, disk->first_minor),
			    disk->minors, NULL, exact_match, exact_lock, disk);
	register_disk(disk);
	blk_register_queue(disk);
192
193
194
195

	bdi = &disk->queue->backing_dev_info;
	bdi_register_dev(bdi, MKDEV(disk->major, disk->first_minor));
	sysfs_create_link(&disk->dev.kobj, &bdi->dev->kobj, "bdi");
Linus Torvalds's avatar
Linus Torvalds committed
196
197
198
199
200
201
202
}

EXPORT_SYMBOL(add_disk);
EXPORT_SYMBOL(del_gendisk);	/* in partitions/check.c */

void unlink_gendisk(struct gendisk *disk)
{
203
204
	sysfs_remove_link(&disk->dev.kobj, "bdi");
	bdi_unregister(&disk->queue->backing_dev_info);
Linus Torvalds's avatar
Linus Torvalds committed
205
206
207
208
209
210
211
212
213
214
215
216
	blk_unregister_queue(disk);
	blk_unregister_region(MKDEV(disk->major, disk->first_minor),
			      disk->minors);
}

/**
 * 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.
 */
217
struct gendisk *get_gendisk(dev_t devt, int *part)
Linus Torvalds's avatar
Linus Torvalds committed
218
{
219
220
221
222
	struct kobject *kobj = kobj_lookup(bdev_map, devt, part);
	struct device *dev = kobj_to_dev(kobj);

	return  kobj ? dev_to_disk(dev) : NULL;
Linus Torvalds's avatar
Linus Torvalds committed
223
224
}

225
226
227
228
229
230
231
/*
 * print a full list of all partitions - intended for places where the root
 * filesystem can't be mounted and thus to give the victim some idea of what
 * went wrong
 */
void __init printk_all_partitions(void)
{
232
	struct device *dev;
233
	struct gendisk *sgp;
234
235
	char buf[BDEVNAME_SIZE];
	int n;
236

237
	mutex_lock(&block_class_lock);
238
	/* For each block device... */
239
240
241
242
	list_for_each_entry(dev, &block_class.devices, node) {
		if (dev->type != &disk_type)
			continue;
		sgp = dev_to_disk(dev);
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
		/*
		 * Don't show empty devices or things that have been surpressed
		 */
		if (get_capacity(sgp) == 0 ||
		    (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO))
			continue;

		/*
		 * Note, unlike /proc/partitions, I am showing the numbers in
		 * hex - the same format as the root= option takes.
		 */
		printk("%02x%02x %10llu %s",
			sgp->major, sgp->first_minor,
			(unsigned long long)get_capacity(sgp) >> 1,
			disk_name(sgp, 0, buf));
		if (sgp->driverfs_dev != NULL &&
		    sgp->driverfs_dev->driver != NULL)
			printk(" driver: %s\n",
				sgp->driverfs_dev->driver->name);
		else
			printk(" (driver?)\n");

		/* now show the partitions */
		for (n = 0; n < sgp->minors - 1; ++n) {
			if (sgp->part[n] == NULL)
				continue;
			if (sgp->part[n]->nr_sects == 0)
				continue;
			printk("  %02x%02x %10llu %s\n",
				sgp->major, n + 1 + sgp->first_minor,
				(unsigned long long)sgp->part[n]->nr_sects >> 1,
				disk_name(sgp, n + 1, buf));
275
276
		}
	}
277

278
	mutex_unlock(&block_class_lock);
279
280
}

Linus Torvalds's avatar
Linus Torvalds committed
281
282
283
284
#ifdef CONFIG_PROC_FS
/* iterator */
static void *part_start(struct seq_file *part, loff_t *pos)
{
285
286
	loff_t k = *pos;
	struct device *dev;
Linus Torvalds's avatar
Linus Torvalds committed
287

288
289
290
291
292
293
294
	mutex_lock(&block_class_lock);
	list_for_each_entry(dev, &block_class.devices, node) {
		if (dev->type != &disk_type)
			continue;
		if (!k--)
			return dev_to_disk(dev);
	}
Linus Torvalds's avatar
Linus Torvalds committed
295
296
297
298
299
	return NULL;
}

static void *part_next(struct seq_file *part, void *v, loff_t *pos)
{
300
301
	struct gendisk *gp = v;
	struct device *dev;
Linus Torvalds's avatar
Linus Torvalds committed
302
	++*pos;
303
304
305
306
307
308
309
	list_for_each_entry(dev, &gp->dev.node, node) {
		if (&dev->node == &block_class.devices)
			return NULL;
		if (dev->type == &disk_type)
			return dev_to_disk(dev);
	}
	return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
310
311
312
313
}

static void part_stop(struct seq_file *part, void *v)
{
314
	mutex_unlock(&block_class_lock);
Linus Torvalds's avatar
Linus Torvalds committed
315
316
317
318
319
320
321
322
}

static int show_partition(struct seq_file *part, void *v)
{
	struct gendisk *sgp = v;
	int n;
	char buf[BDEVNAME_SIZE];

323
	if (&sgp->dev.node == block_class.devices.next)
Linus Torvalds's avatar
Linus Torvalds committed
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
		seq_puts(part, "major minor  #blocks  name\n\n");

	/* Don't show non-partitionable removeable devices or empty devices */
	if (!get_capacity(sgp) ||
			(sgp->minors == 1 && (sgp->flags & GENHD_FL_REMOVABLE)))
		return 0;
	if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO)
		return 0;

	/* show the full disk and all non-0 size partitions of it */
	seq_printf(part, "%4d  %4d %10llu %s\n",
		sgp->major, sgp->first_minor,
		(unsigned long long)get_capacity(sgp) >> 1,
		disk_name(sgp, 0, buf));
	for (n = 0; n < sgp->minors - 1; n++) {
		if (!sgp->part[n])
			continue;
		if (sgp->part[n]->nr_sects == 0)
			continue;
		seq_printf(part, "%4d  %4d %10llu %s\n",
			sgp->major, n + 1 + sgp->first_minor,
			(unsigned long long)sgp->part[n]->nr_sects >> 1 ,
			disk_name(sgp, n + 1, buf));
	}

	return 0;
}

352
const struct seq_operations partitions_op = {
353
354
355
356
	.start	= part_start,
	.next	= part_next,
	.stop	= part_stop,
	.show	= show_partition
Linus Torvalds's avatar
Linus Torvalds committed
357
358
359
360
};
#endif


361
static struct kobject *base_probe(dev_t devt, int *part, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
362
{
363
	if (request_module("block-major-%d-%d", MAJOR(devt), MINOR(devt)) > 0)
Linus Torvalds's avatar
Linus Torvalds committed
364
		/* Make old-style 2.4 aliases work */
365
		request_module("block-major-%d", MAJOR(devt));
Linus Torvalds's avatar
Linus Torvalds committed
366
367
368
369
370
	return NULL;
}

static int __init genhd_device_init(void)
{
Roland McGrath's avatar
Roland McGrath committed
371
372
373
	int error = class_register(&block_class);
	if (unlikely(error))
		return error;
374
	bdev_map = kobj_map_init(base_probe, &block_class_lock);
Linus Torvalds's avatar
Linus Torvalds committed
375
	blk_dev_init();
376
377
378
379
380

#ifndef CONFIG_SYSFS_DEPRECATED
	/* create top-level block dir */
	block_depr = kobject_create_and_add("block", NULL);
#endif
381
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
382
383
384
385
}

subsys_initcall(genhd_device_init);

386
387
static ssize_t disk_range_show(struct device *dev,
			       struct device_attribute *attr, char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
388
{
389
	struct gendisk *disk = dev_to_disk(dev);
Linus Torvalds's avatar
Linus Torvalds committed
390

391
	return sprintf(buf, "%d\n", disk->minors);
Linus Torvalds's avatar
Linus Torvalds committed
392
393
}

394
395
static ssize_t disk_removable_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
396
{
397
	struct gendisk *disk = dev_to_disk(dev);
398

399
400
	return sprintf(buf, "%d\n",
		       (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0));
401
402
}

403
404
static ssize_t disk_size_show(struct device *dev,
			      struct device_attribute *attr, char *buf)
405
{
406
	struct gendisk *disk = dev_to_disk(dev);
Linus Torvalds's avatar
Linus Torvalds committed
407

408
	return sprintf(buf, "%llu\n", (unsigned long long)get_capacity(disk));
Linus Torvalds's avatar
Linus Torvalds committed
409
}
410
411
412

static ssize_t disk_capability_show(struct device *dev,
				    struct device_attribute *attr, char *buf)
413
{
414
415
416
	struct gendisk *disk = dev_to_disk(dev);

	return sprintf(buf, "%x\n", disk->flags);
417
}
418
419
420

static ssize_t disk_stat_show(struct device *dev,
			      struct device_attribute *attr, char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
421
{
422
423
	struct gendisk *disk = dev_to_disk(dev);

Linus Torvalds's avatar
Linus Torvalds committed
424
425
426
	preempt_disable();
	disk_round_stats(disk);
	preempt_enable();
427
	return sprintf(buf,
428
429
		"%8lu %8lu %8llu %8u "
		"%8lu %8lu %8llu %8u "
Linus Torvalds's avatar
Linus Torvalds committed
430
431
		"%8u %8u %8u"
		"\n",
432
433
434
435
436
437
438
439
		disk_stat_read(disk, ios[READ]),
		disk_stat_read(disk, merges[READ]),
		(unsigned long long)disk_stat_read(disk, sectors[READ]),
		jiffies_to_msecs(disk_stat_read(disk, ticks[READ])),
		disk_stat_read(disk, ios[WRITE]),
		disk_stat_read(disk, merges[WRITE]),
		(unsigned long long)disk_stat_read(disk, sectors[WRITE]),
		jiffies_to_msecs(disk_stat_read(disk, ticks[WRITE])),
Linus Torvalds's avatar
Linus Torvalds committed
440
441
442
443
444
		disk->in_flight,
		jiffies_to_msecs(disk_stat_read(disk, io_ticks)),
		jiffies_to_msecs(disk_stat_read(disk, time_in_queue)));
}

445
#ifdef CONFIG_FAIL_MAKE_REQUEST
446
447
448
449
450
451
452
static ssize_t disk_fail_show(struct device *dev,
			      struct device_attribute *attr, char *buf)
{
	struct gendisk *disk = dev_to_disk(dev);

	return sprintf(buf, "%d\n", disk->flags & GENHD_FL_FAIL ? 1 : 0);
}
453

454
455
static ssize_t disk_fail_store(struct device *dev,
			       struct device_attribute *attr,
456
457
			       const char *buf, size_t count)
{
458
	struct gendisk *disk = dev_to_disk(dev);
459
460
461
462
463
464
465
466
467
468
469
470
471
472
	int i;

	if (count > 0 && sscanf(buf, "%d", &i) > 0) {
		if (i == 0)
			disk->flags &= ~GENHD_FL_FAIL;
		else
			disk->flags |= GENHD_FL_FAIL;
	}

	return count;
}

#endif

473
474
475
476
477
static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL);
static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL);
static DEVICE_ATTR(size, S_IRUGO, disk_size_show, NULL);
static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL);
static DEVICE_ATTR(stat, S_IRUGO, disk_stat_show, NULL);
478
#ifdef CONFIG_FAIL_MAKE_REQUEST
479
480
static struct device_attribute dev_attr_fail =
	__ATTR(make-it-fail, S_IRUGO|S_IWUSR, disk_fail_show, disk_fail_store);
481
#endif
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501

static struct attribute *disk_attrs[] = {
	&dev_attr_range.attr,
	&dev_attr_removable.attr,
	&dev_attr_size.attr,
	&dev_attr_capability.attr,
	&dev_attr_stat.attr,
#ifdef CONFIG_FAIL_MAKE_REQUEST
	&dev_attr_fail.attr,
#endif
	NULL
};

static struct attribute_group disk_attr_group = {
	.attrs = disk_attrs,
};

static struct attribute_group *disk_attr_groups[] = {
	&disk_attr_group,
	NULL
Linus Torvalds's avatar
Linus Torvalds committed
502
503
};

504
static void disk_release(struct device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
505
{
506
507
	struct gendisk *disk = dev_to_disk(dev);

Linus Torvalds's avatar
Linus Torvalds committed
508
509
510
511
512
	kfree(disk->random);
	kfree(disk->part);
	free_disk_stats(disk);
	kfree(disk);
}
513
514
struct class block_class = {
	.name		= "block",
Linus Torvalds's avatar
Linus Torvalds committed
515
516
};

Adrian Bunk's avatar
Adrian Bunk committed
517
static struct device_type disk_type = {
518
519
520
	.name		= "disk",
	.groups		= disk_attr_groups,
	.release	= disk_release,
Linus Torvalds's avatar
Linus Torvalds committed
521
522
523
524
525
526
527
528
529
530
531
532
533
};

/*
 * aggregate disk stat collector.  Uses the same stats that the sysfs
 * entries do, above, but makes them available through one seq_file.
 *
 * The output looks suspiciously like /proc/partitions with a bunch of
 * extra fields.
 */

static void *diskstats_start(struct seq_file *part, loff_t *pos)
{
	loff_t k = *pos;
534
	struct device *dev;
Linus Torvalds's avatar
Linus Torvalds committed
535

536
537
538
539
	mutex_lock(&block_class_lock);
	list_for_each_entry(dev, &block_class.devices, node) {
		if (dev->type != &disk_type)
			continue;
Linus Torvalds's avatar
Linus Torvalds committed
540
		if (!k--)
541
542
			return dev_to_disk(dev);
	}
Linus Torvalds's avatar
Linus Torvalds committed
543
544
545
546
547
	return NULL;
}

static void *diskstats_next(struct seq_file *part, void *v, loff_t *pos)
{
548
549
550
	struct gendisk *gp = v;
	struct device *dev;

Linus Torvalds's avatar
Linus Torvalds committed
551
	++*pos;
552
553
554
555
556
557
558
	list_for_each_entry(dev, &gp->dev.node, node) {
		if (&dev->node == &block_class.devices)
			return NULL;
		if (dev->type == &disk_type)
			return dev_to_disk(dev);
	}
	return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
559
560
561
562
}

static void diskstats_stop(struct seq_file *part, void *v)
{
563
	mutex_unlock(&block_class_lock);
Linus Torvalds's avatar
Linus Torvalds committed
564
565
566
567
568
569
570
571
572
}

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

	/*
573
	if (&gp->dev.kobj.entry == block_class.devices.next)
Linus Torvalds's avatar
Linus Torvalds committed
574
575
576
577
578
579
580
581
582
		seq_puts(s,	"major minor name"
				"     rio rmerge rsect ruse wio wmerge "
				"wsect wuse running use aveq"
				"\n\n");
	*/
 
	preempt_disable();
	disk_round_stats(gp);
	preempt_enable();
583
	seq_printf(s, "%4d %4d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n",
Linus Torvalds's avatar
Linus Torvalds committed
584
		gp->major, n + gp->first_minor, disk_name(gp, n, buf),
585
586
587
588
589
590
		disk_stat_read(gp, ios[0]), disk_stat_read(gp, merges[0]),
		(unsigned long long)disk_stat_read(gp, sectors[0]),
		jiffies_to_msecs(disk_stat_read(gp, ticks[0])),
		disk_stat_read(gp, ios[1]), disk_stat_read(gp, merges[1]),
		(unsigned long long)disk_stat_read(gp, sectors[1]),
		jiffies_to_msecs(disk_stat_read(gp, ticks[1])),
Linus Torvalds's avatar
Linus Torvalds committed
591
592
593
594
595
596
597
598
		gp->in_flight,
		jiffies_to_msecs(disk_stat_read(gp, io_ticks)),
		jiffies_to_msecs(disk_stat_read(gp, time_in_queue)));

	/* now show all non-0 size partitions of it */
	for (n = 0; n < gp->minors - 1; n++) {
		struct hd_struct *hd = gp->part[n];

599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
		if (!hd || !hd->nr_sects)
			continue;

		preempt_disable();
		part_round_stats(hd);
		preempt_enable();
		seq_printf(s, "%4d %4d %s %lu %lu %llu "
			   "%u %lu %lu %llu %u %u %u %u\n",
			   gp->major, n + gp->first_minor + 1,
			   disk_name(gp, n + 1, buf),
			   part_stat_read(hd, ios[0]),
			   part_stat_read(hd, merges[0]),
			   (unsigned long long)part_stat_read(hd, sectors[0]),
			   jiffies_to_msecs(part_stat_read(hd, ticks[0])),
			   part_stat_read(hd, ios[1]),
			   part_stat_read(hd, merges[1]),
			   (unsigned long long)part_stat_read(hd, sectors[1]),
			   jiffies_to_msecs(part_stat_read(hd, ticks[1])),
			   hd->in_flight,
			   jiffies_to_msecs(part_stat_read(hd, io_ticks)),
			   jiffies_to_msecs(part_stat_read(hd, time_in_queue))
			);
Linus Torvalds's avatar
Linus Torvalds committed
621
622
623
624
625
	}
 
	return 0;
}

626
const struct seq_operations diskstats_op = {
Linus Torvalds's avatar
Linus Torvalds committed
627
628
629
630
631
632
	.start	= diskstats_start,
	.next	= diskstats_next,
	.stop	= diskstats_stop,
	.show	= diskstats_show
};

633
634
635
636
637
638
639
640
641
642
static void media_change_notify_thread(struct work_struct *work)
{
	struct gendisk *gd = container_of(work, struct gendisk, async_notify);
	char event[] = "MEDIA_CHANGE=1";
	char *envp[] = { event, NULL };

	/*
	 * set enviroment vars to indicate which event this is for
	 * so that user space will know to go check the media status.
	 */
643
	kobject_uevent_env(&gd->dev.kobj, KOBJ_CHANGE, envp);
644
645
646
	put_device(gd->driverfs_dev);
}

Adrian Bunk's avatar
Adrian Bunk committed
647
#if 0
648
649
650
651
652
653
void genhd_media_change_notify(struct gendisk *disk)
{
	get_device(disk->driverfs_dev);
	schedule_work(&disk->async_notify);
}
EXPORT_SYMBOL_GPL(genhd_media_change_notify);
Adrian Bunk's avatar
Adrian Bunk committed
654
#endif  /*  0  */
655

656
dev_t blk_lookup_devt(const char *name, int part)
657
658
659
660
661
662
{
	struct device *dev;
	dev_t devt = MKDEV(0, 0);

	mutex_lock(&block_class_lock);
	list_for_each_entry(dev, &block_class.devices, node) {
663
664
		if (dev->type != &disk_type)
			continue;
665
		if (strcmp(dev->bus_id, name) == 0) {
666
667
668
669
670
			struct gendisk *disk = dev_to_disk(dev);

			if (part < disk->minors)
				devt = MKDEV(MAJOR(dev->devt),
					     MINOR(dev->devt) + part);
671
672
673
674
675
676
677
678
679
			break;
		}
	}
	mutex_unlock(&block_class_lock);

	return devt;
}
EXPORT_SYMBOL(blk_lookup_devt);

Linus Torvalds's avatar
Linus Torvalds committed
680
681
struct gendisk *alloc_disk(int minors)
{
682
683
684
685
686
687
688
	return alloc_disk_node(minors, -1);
}

struct gendisk *alloc_disk_node(int minors, int node_id)
{
	struct gendisk *disk;

689
690
	disk = kmalloc_node(sizeof(struct gendisk),
				GFP_KERNEL | __GFP_ZERO, node_id);
Linus Torvalds's avatar
Linus Torvalds committed
691
692
693
694
695
696
697
	if (disk) {
		if (!init_disk_stats(disk)) {
			kfree(disk);
			return NULL;
		}
		if (minors > 1) {
			int size = (minors - 1) * sizeof(struct hd_struct *);
698
699
			disk->part = kmalloc_node(size,
				GFP_KERNEL | __GFP_ZERO, node_id);
Linus Torvalds's avatar
Linus Torvalds committed
700
			if (!disk->part) {
701
				free_disk_stats(disk);
Linus Torvalds's avatar
Linus Torvalds committed
702
703
704
705
706
707
				kfree(disk);
				return NULL;
			}
		}
		disk->minors = minors;
		rand_initialize_disk(disk);
708
709
710
		disk->dev.class = &block_class;
		disk->dev.type = &disk_type;
		device_initialize(&disk->dev);
711
712
		INIT_WORK(&disk->async_notify,
			media_change_notify_thread);
Linus Torvalds's avatar
Linus Torvalds committed
713
714
715
716
717
	}
	return disk;
}

EXPORT_SYMBOL(alloc_disk);
718
EXPORT_SYMBOL(alloc_disk_node);
Linus Torvalds's avatar
Linus Torvalds committed
719
720
721
722
723
724
725
726
727
728
729

struct kobject *get_disk(struct gendisk *disk)
{
	struct module *owner;
	struct kobject *kobj;

	if (!disk->fops)
		return NULL;
	owner = disk->fops->owner;
	if (owner && !try_module_get(owner))
		return NULL;
730
	kobj = kobject_get(&disk->dev.kobj);
Linus Torvalds's avatar
Linus Torvalds committed
731
732
733
734
735
736
737
738
739
740
741
742
743
	if (kobj == NULL) {
		module_put(owner);
		return NULL;
	}
	return kobj;

}

EXPORT_SYMBOL(get_disk);

void put_disk(struct gendisk *disk)
{
	if (disk)
744
		kobject_put(&disk->dev.kobj);
Linus Torvalds's avatar
Linus Torvalds committed
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
}

EXPORT_SYMBOL(put_disk);

void set_device_ro(struct block_device *bdev, int flag)
{
	if (bdev->bd_contains != bdev)
		bdev->bd_part->policy = flag;
	else
		bdev->bd_disk->policy = flag;
}

EXPORT_SYMBOL(set_device_ro);

void set_disk_ro(struct gendisk *disk, int flag)
{
	int i;
	disk->policy = flag;
	for (i = 0; i < disk->minors - 1; i++)
		if (disk->part[i]) disk->part[i]->policy = flag;
}

EXPORT_SYMBOL(set_disk_ro);

int bdev_read_only(struct block_device *bdev)
{
	if (!bdev)
		return 0;
	else if (bdev->bd_contains != bdev)
		return bdev->bd_part->policy;
	else
		return bdev->bd_disk->policy;
}

EXPORT_SYMBOL(bdev_read_only);

int invalidate_partition(struct gendisk *disk, int index)
{
	int res = 0;
	struct block_device *bdev = bdget_disk(disk, index);
	if (bdev) {
786
787
		fsync_bdev(bdev);
		res = __invalidate_device(bdev);
Linus Torvalds's avatar
Linus Torvalds committed
788
789
790
791
792
793
		bdput(bdev);
	}
	return res;
}

EXPORT_SYMBOL(invalidate_partition);