Blame view

block/genhd.c 19 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /*
   *  gendisk handling
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
  #include <linux/module.h>
  #include <linux/fs.h>
  #include <linux/genhd.h>
b446b60e4   Andrew Morton   [PATCH] rework re...
7
  #include <linux/kdev_t.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
13
14
15
  #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>
2ef41634d   Christoph Hellwig   [PATCH] remove do...
16
  #include <linux/buffer_head.h>
58383af62   Jes Sorensen   [PATCH] kobj_map ...
17
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18

823bccfc4   Greg Kroah-Hartman   remove "struct su...
19
  struct kset block_subsys;
58383af62   Jes Sorensen   [PATCH] kobj_map ...
20
  static DEFINE_MUTEX(block_subsys_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
24
25
26
27
28
29
  
  /*
   * Can be deleted altogether. Later.
   *
   */
  static struct blk_major_name {
  	struct blk_major_name *next;
  	int major;
  	char name[16];
68eef3b47   Joe Korty   [PATCH] Simplify ...
30
  } *major_names[BLKDEV_MAJOR_HASH_SIZE];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
33
34
  
  /* index in the above - for now: assume no multimajor ranges */
  static inline int major_to_index(int major)
  {
68eef3b47   Joe Korty   [PATCH] Simplify ...
35
  	return major % BLKDEV_MAJOR_HASH_SIZE;
7170be5f5   Neil Horman   [PATCH] convert /...
36
  }
68eef3b47   Joe Korty   [PATCH] Simplify ...
37
  #ifdef CONFIG_PROC_FS
7170be5f5   Neil Horman   [PATCH] convert /...
38

68eef3b47   Joe Korty   [PATCH] Simplify ...
39
  void blkdev_show(struct seq_file *f, off_t offset)
7170be5f5   Neil Horman   [PATCH] convert /...
40
  {
68eef3b47   Joe Korty   [PATCH] Simplify ...
41
  	struct blk_major_name *dp;
7170be5f5   Neil Horman   [PATCH] convert /...
42

68eef3b47   Joe Korty   [PATCH] Simplify ...
43
44
45
46
47
48
  	if (offset < BLKDEV_MAJOR_HASH_SIZE) {
  		mutex_lock(&block_subsys_lock);
  		for (dp = major_names[offset]; dp; dp = dp->next)
  			seq_printf(f, "%3d %s
  ", dp->major, dp->name);
  		mutex_unlock(&block_subsys_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
  }
7170be5f5   Neil Horman   [PATCH] convert /...
51

68eef3b47   Joe Korty   [PATCH] Simplify ...
52
  #endif /* CONFIG_PROC_FS */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
55
56
57
  
  int register_blkdev(unsigned int major, const char *name)
  {
  	struct blk_major_name **n, *p;
  	int index, ret = 0;
58383af62   Jes Sorensen   [PATCH] kobj_map ...
58
  	mutex_lock(&block_subsys_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  
  	/* 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
  ",
  			       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
  ",
  		       major, name);
  		kfree(p);
  	}
  out:
58383af62   Jes Sorensen   [PATCH] kobj_map ...
105
  	mutex_unlock(&block_subsys_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
109
  	return ret;
  }
  
  EXPORT_SYMBOL(register_blkdev);
f4480240f   Akinobu Mita   unregister_blkdev...
110
  void unregister_blkdev(unsigned int major, const char *name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
113
114
  {
  	struct blk_major_name **n;
  	struct blk_major_name *p = NULL;
  	int index = major_to_index(major);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115

58383af62   Jes Sorensen   [PATCH] kobj_map ...
116
  	mutex_lock(&block_subsys_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
119
  	for (n = &major_names[index]; *n; n = &(*n)->next)
  		if ((*n)->major == major)
  			break;
294462a5c   Akinobu Mita   unregister_blkdev...
120
121
  	if (!*n || strcmp((*n)->name, name)) {
  		WARN_ON(1);
294462a5c   Akinobu Mita   unregister_blkdev...
122
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
124
125
  		p = *n;
  		*n = p->next;
  	}
58383af62   Jes Sorensen   [PATCH] kobj_map ...
126
  	mutex_unlock(&block_subsys_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
  	kfree(p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  }
  
  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.
   */
  void blk_register_region(dev_t dev, unsigned long range, struct module *module,
  			 struct kobject *(*probe)(dev_t, int *, void *),
  			 int (*lock)(dev_t, void *), void *data)
  {
  	kobj_map(bdev_map, dev, range, module, probe, lock, data);
  }
  
  EXPORT_SYMBOL(blk_register_region);
  
  void blk_unregister_region(dev_t dev, unsigned long range)
  {
  	kobj_unmap(bdev_map, dev, range);
  }
  
  EXPORT_SYMBOL(blk_unregister_region);
  
  static struct kobject *exact_match(dev_t dev, int *part, void *data)
  {
  	struct gendisk *p = data;
  	return &p->kobj;
  }
  
  static int exact_lock(dev_t dev, void *data)
  {
  	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)
  {
  	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);
  }
  
  EXPORT_SYMBOL(add_disk);
  EXPORT_SYMBOL(del_gendisk);	/* in partitions/check.c */
  
  void unlink_gendisk(struct gendisk *disk)
  {
  	blk_unregister_queue(disk);
  	blk_unregister_region(MKDEV(disk->major, disk->first_minor),
  			      disk->minors);
  }
  
  #define to_disk(obj) container_of(obj,struct gendisk,kobj)
  
  /**
   * 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(dev_t dev, int *part)
  {
  	struct kobject *kobj = kobj_lookup(bdev_map, dev, part);
  	return  kobj ? to_disk(kobj) : NULL;
  }
dd2a345f8   Dave Gilbert   Display all possi...
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
259
260
261
262
263
264
  /*
   * 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)
  {
  	int n;
  	struct gendisk *sgp;
  
  	mutex_lock(&block_subsys_lock);
  	/* For each block device... */
  	list_for_each_entry(sgp, &block_subsys.list, kobj.entry) {
  		char buf[BDEVNAME_SIZE];
  		/*
  		 * 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
  ",
  				sgp->driverfs_dev->driver->name);
  		else
  			printk(" (driver?)
  ");
  
  		/* 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
  ",
  				sgp->major, n + 1 + sgp->first_minor,
  				(unsigned long long)sgp->part[n]->nr_sects >> 1,
  				disk_name(sgp, n + 1, buf));
  		} /* partition subloop */
  	} /* Block device loop */
  
  	mutex_unlock(&block_subsys_lock);
  	return;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
269
270
  #ifdef CONFIG_PROC_FS
  /* iterator */
  static void *part_start(struct seq_file *part, loff_t *pos)
  {
  	struct list_head *p;
  	loff_t l = *pos;
58383af62   Jes Sorensen   [PATCH] kobj_map ...
271
  	mutex_lock(&block_subsys_lock);
823bccfc4   Greg Kroah-Hartman   remove "struct su...
272
  	list_for_each(p, &block_subsys.list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
276
277
278
279
280
281
  		if (!l--)
  			return list_entry(p, struct gendisk, kobj.entry);
  	return NULL;
  }
  
  static void *part_next(struct seq_file *part, void *v, loff_t *pos)
  {
  	struct list_head *p = ((struct gendisk *)v)->kobj.entry.next;
  	++*pos;
823bccfc4   Greg Kroah-Hartman   remove "struct su...
282
  	return p==&block_subsys.list ? NULL :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
286
287
  		list_entry(p, struct gendisk, kobj.entry);
  }
  
  static void part_stop(struct seq_file *part, void *v)
  {
58383af62   Jes Sorensen   [PATCH] kobj_map ...
288
  	mutex_unlock(&block_subsys_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
291
292
293
294
295
  }
  
  static int show_partition(struct seq_file *part, void *v)
  {
  	struct gendisk *sgp = v;
  	int n;
  	char buf[BDEVNAME_SIZE];
823bccfc4   Greg Kroah-Hartman   remove "struct su...
296
  	if (&sgp->kobj.entry == block_subsys.list.next)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
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
  		seq_puts(part, "major minor  #blocks  name
  
  ");
  
  	/* 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
  ",
  		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
  ",
  			sgp->major, n + 1 + sgp->first_minor,
  			(unsigned long long)sgp->part[n]->nr_sects >> 1 ,
  			disk_name(sgp, n + 1, buf));
  	}
  
  	return 0;
  }
  
  struct seq_operations partitions_op = {
  	.start =part_start,
  	.next =	part_next,
  	.stop =	part_stop,
  	.show =	show_partition
  };
  #endif
  
  
  extern int blk_dev_init(void);
  
  static struct kobject *base_probe(dev_t dev, int *part, void *data)
  {
  	if (request_module("block-major-%d-%d", MAJOR(dev), MINOR(dev)) > 0)
  		/* Make old-style 2.4 aliases work */
  		request_module("block-major-%d", MAJOR(dev));
  	return NULL;
  }
  
  static int __init genhd_device_init(void)
  {
87a572611   Randy Dunlap   [PATCH] block: ha...
350
  	int err;
58383af62   Jes Sorensen   [PATCH] kobj_map ...
351
  	bdev_map = kobj_map_init(base_probe, &block_subsys_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
  	blk_dev_init();
87a572611   Randy Dunlap   [PATCH] block: ha...
353
354
355
356
357
358
  	err = subsystem_register(&block_subsys);
  	if (err < 0)
  		printk(KERN_WARNING "%s: subsystem_register error: %d
  ",
  			__FUNCTION__, err);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
  }
  
  subsys_initcall(genhd_device_init);
  
  
  
  /*
   * kobject & sysfs bindings for block devices
   */
  static ssize_t disk_attr_show(struct kobject *kobj, struct attribute *attr,
  			      char *page)
  {
  	struct gendisk *disk = to_disk(kobj);
  	struct disk_attribute *disk_attr =
  		container_of(attr,struct disk_attribute,attr);
6c1852a08   Dmitry Torokhov   [PATCH] sysfs: (d...
374
  	ssize_t ret = -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
376
377
378
379
  
  	if (disk_attr->show)
  		ret = disk_attr->show(disk,page);
  	return ret;
  }
a7fd67062   Kay Sievers   [PATCH] add sysfs...
380
381
382
383
384
385
386
387
388
389
390
391
  static ssize_t disk_attr_store(struct kobject * kobj, struct attribute * attr,
  			       const char *page, size_t count)
  {
  	struct gendisk *disk = to_disk(kobj);
  	struct disk_attribute *disk_attr =
  		container_of(attr,struct disk_attribute,attr);
  	ssize_t ret = 0;
  
  	if (disk_attr->store)
  		ret = disk_attr->store(disk, page, count);
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
  static struct sysfs_ops disk_sysfs_ops = {
  	.show	= &disk_attr_show,
a7fd67062   Kay Sievers   [PATCH] add sysfs...
394
  	.store	= &disk_attr_store,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
395
  };
a7fd67062   Kay Sievers   [PATCH] add sysfs...
396
397
398
  static ssize_t disk_uevent_store(struct gendisk * disk,
  				 const char *buf, size_t count)
  {
312c004d3   Kay Sievers   [PATCH] driver co...
399
  	kobject_uevent(&disk->kobj, KOBJ_ADD);
a7fd67062   Kay Sievers   [PATCH] add sysfs...
400
401
  	return count;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
  static ssize_t disk_dev_read(struct gendisk * disk, char *page)
  {
  	dev_t base = MKDEV(disk->major, disk->first_minor); 
  	return print_dev_t(page, base);
  }
  static ssize_t disk_range_read(struct gendisk * disk, char *page)
  {
  	return sprintf(page, "%d
  ", disk->minors);
  }
  static ssize_t disk_removable_read(struct gendisk * disk, char *page)
  {
  	return sprintf(page, "%d
  ",
  		       (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0));
  
  }
  static ssize_t disk_size_read(struct gendisk * disk, char *page)
  {
  	return sprintf(page, "%llu
  ", (unsigned long long)get_capacity(disk));
  }
86ce18d7b   Kristen Carlson Accardi   genhd: expose AN ...
424
425
426
427
428
  static ssize_t disk_capability_read(struct gendisk *disk, char *page)
  {
  	return sprintf(page, "%x
  ", disk->flags);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
431
432
433
434
  static ssize_t disk_stats_read(struct gendisk * disk, char *page)
  {
  	preempt_disable();
  	disk_round_stats(disk);
  	preempt_enable();
  	return sprintf(page,
837c78787   Ben Woodard   [BLOCK] increase ...
435
436
  		"%8lu %8lu %8llu %8u "
  		"%8lu %8lu %8llu %8u "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
438
439
  		"%8u %8u %8u"
  		"
  ",
47a004103   Jens Axboe   [BLOCK] Document ...
440
441
442
443
444
445
446
447
  		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])),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
451
  		disk->in_flight,
  		jiffies_to_msecs(disk_stat_read(disk, io_ticks)),
  		jiffies_to_msecs(disk_stat_read(disk, time_in_queue)));
  }
a7fd67062   Kay Sievers   [PATCH] add sysfs...
452
453
454
455
  static struct disk_attribute disk_attr_uevent = {
  	.attr = {.name = "uevent", .mode = S_IWUSR },
  	.store	= disk_uevent_store
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
  static struct disk_attribute disk_attr_dev = {
  	.attr = {.name = "dev", .mode = S_IRUGO },
  	.show	= disk_dev_read
  };
  static struct disk_attribute disk_attr_range = {
  	.attr = {.name = "range", .mode = S_IRUGO },
  	.show	= disk_range_read
  };
  static struct disk_attribute disk_attr_removable = {
  	.attr = {.name = "removable", .mode = S_IRUGO },
  	.show	= disk_removable_read
  };
  static struct disk_attribute disk_attr_size = {
  	.attr = {.name = "size", .mode = S_IRUGO },
  	.show	= disk_size_read
  };
86ce18d7b   Kristen Carlson Accardi   genhd: expose AN ...
472
473
474
475
  static struct disk_attribute disk_attr_capability = {
  	.attr = {.name = "capability", .mode = S_IRUGO },
  	.show	= disk_capability_read
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
477
478
479
  static struct disk_attribute disk_attr_stat = {
  	.attr = {.name = "stat", .mode = S_IRUGO },
  	.show	= disk_stats_read
  };
c17bb4951   Akinobu Mita   [PATCH] fault-inj...
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
  #ifdef CONFIG_FAIL_MAKE_REQUEST
  
  static ssize_t disk_fail_store(struct gendisk * disk,
  			       const char *buf, size_t count)
  {
  	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;
  }
  static ssize_t disk_fail_read(struct gendisk * disk, char *page)
  {
  	return sprintf(page, "%d
  ", disk->flags & GENHD_FL_FAIL ? 1 : 0);
  }
  static struct disk_attribute disk_attr_fail = {
  	.attr = {.name = "make-it-fail", .mode = S_IRUGO | S_IWUSR },
  	.store	= disk_fail_store,
  	.show	= disk_fail_read
  };
  
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
  static struct attribute * default_attrs[] = {
a7fd67062   Kay Sievers   [PATCH] add sysfs...
509
  	&disk_attr_uevent.attr,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
511
512
513
514
  	&disk_attr_dev.attr,
  	&disk_attr_range.attr,
  	&disk_attr_removable.attr,
  	&disk_attr_size.attr,
  	&disk_attr_stat.attr,
86ce18d7b   Kristen Carlson Accardi   genhd: expose AN ...
515
  	&disk_attr_capability.attr,
c17bb4951   Akinobu Mita   [PATCH] fault-inj...
516
517
518
  #ifdef CONFIG_FAIL_MAKE_REQUEST
  	&disk_attr_fail.attr,
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
  	NULL,
  };
  
  static void disk_release(struct kobject * kobj)
  {
  	struct gendisk *disk = to_disk(kobj);
  	kfree(disk->random);
  	kfree(disk->part);
  	free_disk_stats(disk);
  	kfree(disk);
  }
  
  static struct kobj_type ktype_block = {
  	.release	= disk_release,
  	.sysfs_ops	= &disk_sysfs_ops,
  	.default_attrs	= default_attrs,
  };
  
  extern struct kobj_type ktype_part;
312c004d3   Kay Sievers   [PATCH] driver co...
538
  static int block_uevent_filter(struct kset *kset, struct kobject *kobj)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
539
540
541
542
543
  {
  	struct kobj_type *ktype = get_ktype(kobj);
  
  	return ((ktype == &ktype_block) || (ktype == &ktype_part));
  }
7eff2e7a8   Kay Sievers   Driver core: chan...
544
545
  static int block_uevent(struct kset *kset, struct kobject *kobj,
  			struct kobj_uevent_env *env)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
547
548
549
550
  {
  	struct kobj_type *ktype = get_ktype(kobj);
  	struct device *physdev;
  	struct gendisk *disk;
  	struct hd_struct *part;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
552
553
  
  	if (ktype == &ktype_block) {
  		disk = container_of(kobj, struct gendisk, kobj);
7eff2e7a8   Kay Sievers   Driver core: chan...
554
  		add_uevent_var(env, "MINOR=%u", disk->first_minor);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555
556
557
  	} else if (ktype == &ktype_part) {
  		disk = container_of(kobj->parent, struct gendisk, kobj);
  		part = container_of(kobj, struct hd_struct, kobj);
7eff2e7a8   Kay Sievers   Driver core: chan...
558
  		add_uevent_var(env, "MINOR=%u",
312c004d3   Kay Sievers   [PATCH] driver co...
559
  			       disk->first_minor + part->partno);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560
561
  	} else
  		return 0;
7eff2e7a8   Kay Sievers   Driver core: chan...
562
  	add_uevent_var(env, "MAJOR=%u", disk->major);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
564
565
566
567
  
  	/* add physical device, backing this device  */
  	physdev = disk->driverfs_dev;
  	if (physdev) {
  		char *path = kobject_get_path(&physdev->kobj, GFP_KERNEL);
7eff2e7a8   Kay Sievers   Driver core: chan...
568
  		add_uevent_var(env, "PHYSDEVPATH=%s", path);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
570
571
  		kfree(path);
  
  		if (physdev->bus)
7eff2e7a8   Kay Sievers   Driver core: chan...
572
  			add_uevent_var(env, "PHYSDEVBUS=%s", physdev->bus->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
574
  
  		if (physdev->driver)
7eff2e7a8   Kay Sievers   Driver core: chan...
575
  			add_uevent_var(env, physdev->driver->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
578
  	return 0;
  }
312c004d3   Kay Sievers   [PATCH] driver co...
579
580
581
  static struct kset_uevent_ops block_uevent_ops = {
  	.filter		= block_uevent_filter,
  	.uevent		= block_uevent,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
  };
b9d9c82b4   Kay Sievers   [PATCH] Driver co...
583
  decl_subsys(block, &ktype_block, &block_uevent_ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
  
  /*
   * aggregate disk stat collector.  Uses the same stats that the sysfs
   * entries do, above, but makes them available through one seq_file.
   * Watching a few disks may be efficient through sysfs, but watching
   * all of them will be more efficient through this interface.
   *
   * The output looks suspiciously like /proc/partitions with a bunch of
   * extra fields.
   */
  
  /* iterator */
  static void *diskstats_start(struct seq_file *part, loff_t *pos)
  {
  	loff_t k = *pos;
  	struct list_head *p;
58383af62   Jes Sorensen   [PATCH] kobj_map ...
600
  	mutex_lock(&block_subsys_lock);
823bccfc4   Greg Kroah-Hartman   remove "struct su...
601
  	list_for_each(p, &block_subsys.list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
603
604
605
606
607
608
609
610
  		if (!k--)
  			return list_entry(p, struct gendisk, kobj.entry);
  	return NULL;
  }
  
  static void *diskstats_next(struct seq_file *part, void *v, loff_t *pos)
  {
  	struct list_head *p = ((struct gendisk *)v)->kobj.entry.next;
  	++*pos;
823bccfc4   Greg Kroah-Hartman   remove "struct su...
611
  	return p==&block_subsys.list ? NULL :
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612
613
614
615
616
  		list_entry(p, struct gendisk, kobj.entry);
  }
  
  static void diskstats_stop(struct seq_file *part, void *v)
  {
58383af62   Jes Sorensen   [PATCH] kobj_map ...
617
  	mutex_unlock(&block_subsys_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
  }
  
  static int diskstats_show(struct seq_file *s, void *v)
  {
  	struct gendisk *gp = v;
  	char buf[BDEVNAME_SIZE];
  	int n = 0;
  
  	/*
  	if (&sgp->kobj.entry == block_subsys.kset.list.next)
  		seq_puts(s,	"major minor name"
  				"     rio rmerge rsect ruse wio wmerge "
  				"wsect wuse running use aveq"
  				"
  
  ");
  	*/
   
  	preempt_disable();
  	disk_round_stats(gp);
  	preempt_enable();
837c78787   Ben Woodard   [BLOCK] increase ...
639
640
  	seq_printf(s, "%4d %4d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
  		gp->major, n + gp->first_minor, disk_name(gp, n, buf),
a362357b6   Jens Axboe   [BLOCK] Unify the...
642
643
644
645
646
647
  		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])),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
649
650
651
652
653
654
655
656
657
658
659
660
  		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];
  
  		if (hd && hd->nr_sects)
  			seq_printf(s, "%4d %4d %s %u %u %u %u
  ",
  				gp->major, n + gp->first_minor + 1,
  				disk_name(gp, n + 1, buf),
a362357b6   Jens Axboe   [BLOCK] Unify the...
661
662
  				hd->ios[0], hd->sectors[0],
  				hd->ios[1], hd->sectors[1]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
664
665
666
667
668
669
670
671
672
673
  	}
   
  	return 0;
  }
  
  struct seq_operations diskstats_op = {
  	.start	= diskstats_start,
  	.next	= diskstats_next,
  	.stop	= diskstats_stop,
  	.show	= diskstats_show
  };
8ce7ad7b2   Kristen Carlson Accardi   genhd: send async...
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
  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.
  	 */
  	kobject_uevent_env(&gd->kobj, KOBJ_CHANGE, envp);
  	put_device(gd->driverfs_dev);
  }
  
  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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
695
  struct gendisk *alloc_disk(int minors)
  {
1946089a1   Christoph Lameter   [PATCH] NUMA awar...
696
697
698
699
700
701
  	return alloc_disk_node(minors, -1);
  }
  
  struct gendisk *alloc_disk_node(int minors, int node_id)
  {
  	struct gendisk *disk;
94f6030ca   Christoph Lameter   Slab allocators: ...
702
703
  	disk = kmalloc_node(sizeof(struct gendisk),
  				GFP_KERNEL | __GFP_ZERO, node_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
  	if (disk) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
706
707
708
709
710
  		if (!init_disk_stats(disk)) {
  			kfree(disk);
  			return NULL;
  		}
  		if (minors > 1) {
  			int size = (minors - 1) * sizeof(struct hd_struct *);
94f6030ca   Christoph Lameter   Slab allocators: ...
711
712
  			disk->part = kmalloc_node(size,
  				GFP_KERNEL | __GFP_ZERO, node_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
713
  			if (!disk->part) {
c7674030e   Jerome Marchand   block: Fix memory...
714
  				free_disk_stats(disk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
716
717
  				kfree(disk);
  				return NULL;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
719
720
721
722
  		}
  		disk->minors = minors;
  		kobj_set_kset_s(disk,block_subsys);
  		kobject_init(&disk->kobj);
  		rand_initialize_disk(disk);
8ce7ad7b2   Kristen Carlson Accardi   genhd: send async...
723
724
  		INIT_WORK(&disk->async_notify,
  			media_change_notify_thread);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
725
726
727
728
729
  	}
  	return disk;
  }
  
  EXPORT_SYMBOL(alloc_disk);
1946089a1   Christoph Lameter   [PATCH] NUMA awar...
730
  EXPORT_SYMBOL(alloc_disk_node);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
731
732
733
734
735
736
737
738
739
740
741
742
743
744
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
786
787
788
789
790
791
792
793
794
795
796
797
  
  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;
  	kobj = kobject_get(&disk->kobj);
  	if (kobj == NULL) {
  		module_put(owner);
  		return NULL;
  	}
  	return kobj;
  
  }
  
  EXPORT_SYMBOL(get_disk);
  
  void put_disk(struct gendisk *disk)
  {
  	if (disk)
  		kobject_put(&disk->kobj);
  }
  
  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) {
2ef41634d   Christoph Hellwig   [PATCH] remove do...
798
799
  		fsync_bdev(bdev);
  		res = __invalidate_device(bdev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800
801
802
803
804
805
  		bdput(bdev);
  	}
  	return res;
  }
  
  EXPORT_SYMBOL(invalidate_partition);