diff options
Diffstat (limited to 'block/genhd.c')
-rw-r--r-- | block/genhd.c | 217 |
1 files changed, 131 insertions, 86 deletions
diff --git a/block/genhd.c b/block/genhd.c index b922d4801c87..c13cc77291af 100644 --- a/block/genhd.c +++ b/block/genhd.c | |||
@@ -183,16 +183,19 @@ static int exact_lock(dev_t devt, void *data) | |||
183 | void add_disk(struct gendisk *disk) | 183 | void add_disk(struct gendisk *disk) |
184 | { | 184 | { |
185 | struct backing_dev_info *bdi; | 185 | struct backing_dev_info *bdi; |
186 | int retval; | ||
186 | 187 | ||
187 | disk->flags |= GENHD_FL_UP; | 188 | disk->flags |= GENHD_FL_UP; |
188 | blk_register_region(MKDEV(disk->major, disk->first_minor), | 189 | blk_register_region(MKDEV(disk->major, disk->first_minor), |
189 | disk->minors, NULL, exact_match, exact_lock, disk); | 190 | disk->minors, NULL, exact_match, exact_lock, disk); |
190 | register_disk(disk); | 191 | register_disk(disk); |
191 | blk_register_queue(disk); | 192 | blk_register_queue(disk); |
193 | blk_register_filter(disk); | ||
192 | 194 | ||
193 | bdi = &disk->queue->backing_dev_info; | 195 | bdi = &disk->queue->backing_dev_info; |
194 | bdi_register_dev(bdi, MKDEV(disk->major, disk->first_minor)); | 196 | bdi_register_dev(bdi, MKDEV(disk->major, disk->first_minor)); |
195 | sysfs_create_link(&disk->dev.kobj, &bdi->dev->kobj, "bdi"); | 197 | retval = sysfs_create_link(&disk->dev.kobj, &bdi->dev->kobj, "bdi"); |
198 | WARN_ON(retval); | ||
196 | } | 199 | } |
197 | 200 | ||
198 | EXPORT_SYMBOL(add_disk); | 201 | EXPORT_SYMBOL(add_disk); |
@@ -200,6 +203,7 @@ EXPORT_SYMBOL(del_gendisk); /* in partitions/check.c */ | |||
200 | 203 | ||
201 | void unlink_gendisk(struct gendisk *disk) | 204 | void unlink_gendisk(struct gendisk *disk) |
202 | { | 205 | { |
206 | blk_unregister_filter(disk); | ||
203 | sysfs_remove_link(&disk->dev.kobj, "bdi"); | 207 | sysfs_remove_link(&disk->dev.kobj, "bdi"); |
204 | bdi_unregister(&disk->queue->backing_dev_info); | 208 | bdi_unregister(&disk->queue->backing_dev_info); |
205 | blk_unregister_queue(disk); | 209 | blk_unregister_queue(disk); |
@@ -223,89 +227,111 @@ struct gendisk *get_gendisk(dev_t devt, int *part) | |||
223 | } | 227 | } |
224 | 228 | ||
225 | /* | 229 | /* |
226 | * print a full list of all partitions - intended for places where the root | 230 | * print a partitions - intended for places where the root filesystem can't be |
227 | * filesystem can't be mounted and thus to give the victim some idea of what | 231 | * mounted and thus to give the victim some idea of what went wrong |
228 | * went wrong | ||
229 | */ | 232 | */ |
230 | void __init printk_all_partitions(void) | 233 | static int printk_partition(struct device *dev, void *data) |
231 | { | 234 | { |
232 | struct device *dev; | ||
233 | struct gendisk *sgp; | 235 | struct gendisk *sgp; |
234 | char buf[BDEVNAME_SIZE]; | 236 | char buf[BDEVNAME_SIZE]; |
235 | int n; | 237 | int n; |
236 | 238 | ||
237 | mutex_lock(&block_class_lock); | 239 | if (dev->type != &disk_type) |
238 | /* For each block device... */ | 240 | goto exit; |
239 | list_for_each_entry(dev, &block_class.devices, node) { | ||
240 | if (dev->type != &disk_type) | ||
241 | continue; | ||
242 | sgp = dev_to_disk(dev); | ||
243 | /* | ||
244 | * Don't show empty devices or things that have been surpressed | ||
245 | */ | ||
246 | if (get_capacity(sgp) == 0 || | ||
247 | (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO)) | ||
248 | continue; | ||
249 | 241 | ||
250 | /* | 242 | sgp = dev_to_disk(dev); |
251 | * Note, unlike /proc/partitions, I am showing the numbers in | 243 | /* |
252 | * hex - the same format as the root= option takes. | 244 | * Don't show empty devices or things that have been surpressed |
253 | */ | 245 | */ |
254 | printk("%02x%02x %10llu %s", | 246 | if (get_capacity(sgp) == 0 || |
255 | sgp->major, sgp->first_minor, | 247 | (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO)) |
256 | (unsigned long long)get_capacity(sgp) >> 1, | 248 | goto exit; |
257 | disk_name(sgp, 0, buf)); | 249 | |
258 | if (sgp->driverfs_dev != NULL && | 250 | /* |
259 | sgp->driverfs_dev->driver != NULL) | 251 | * Note, unlike /proc/partitions, I am showing the numbers in |
260 | printk(" driver: %s\n", | 252 | * hex - the same format as the root= option takes. |
261 | sgp->driverfs_dev->driver->name); | 253 | */ |
262 | else | 254 | printk("%02x%02x %10llu %s", |
263 | printk(" (driver?)\n"); | 255 | sgp->major, sgp->first_minor, |
264 | 256 | (unsigned long long)get_capacity(sgp) >> 1, | |
265 | /* now show the partitions */ | 257 | disk_name(sgp, 0, buf)); |
266 | for (n = 0; n < sgp->minors - 1; ++n) { | 258 | if (sgp->driverfs_dev != NULL && |
267 | if (sgp->part[n] == NULL) | 259 | sgp->driverfs_dev->driver != NULL) |
268 | continue; | 260 | printk(" driver: %s\n", |
269 | if (sgp->part[n]->nr_sects == 0) | 261 | sgp->driverfs_dev->driver->name); |
270 | continue; | 262 | else |
271 | printk(" %02x%02x %10llu %s\n", | 263 | printk(" (driver?)\n"); |
272 | sgp->major, n + 1 + sgp->first_minor, | 264 | |
273 | (unsigned long long)sgp->part[n]->nr_sects >> 1, | 265 | /* now show the partitions */ |
274 | disk_name(sgp, n + 1, buf)); | 266 | for (n = 0; n < sgp->minors - 1; ++n) { |
275 | } | 267 | if (sgp->part[n] == NULL) |
268 | goto exit; | ||
269 | if (sgp->part[n]->nr_sects == 0) | ||
270 | goto exit; | ||
271 | printk(" %02x%02x %10llu %s\n", | ||
272 | sgp->major, n + 1 + sgp->first_minor, | ||
273 | (unsigned long long)sgp->part[n]->nr_sects >> 1, | ||
274 | disk_name(sgp, n + 1, buf)); | ||
276 | } | 275 | } |
276 | exit: | ||
277 | return 0; | ||
278 | } | ||
277 | 279 | ||
280 | /* | ||
281 | * print a full list of all partitions - intended for places where the root | ||
282 | * filesystem can't be mounted and thus to give the victim some idea of what | ||
283 | * went wrong | ||
284 | */ | ||
285 | void __init printk_all_partitions(void) | ||
286 | { | ||
287 | mutex_lock(&block_class_lock); | ||
288 | class_for_each_device(&block_class, NULL, NULL, printk_partition); | ||
278 | mutex_unlock(&block_class_lock); | 289 | mutex_unlock(&block_class_lock); |
279 | } | 290 | } |
280 | 291 | ||
281 | #ifdef CONFIG_PROC_FS | 292 | #ifdef CONFIG_PROC_FS |
282 | /* iterator */ | 293 | /* iterator */ |
294 | static int find_start(struct device *dev, void *data) | ||
295 | { | ||
296 | loff_t k = *(loff_t *)data; | ||
297 | |||
298 | if (dev->type != &disk_type) | ||
299 | return 0; | ||
300 | if (!k--) | ||
301 | return 1; | ||
302 | return 0; | ||
303 | } | ||
304 | |||
283 | static void *part_start(struct seq_file *part, loff_t *pos) | 305 | static void *part_start(struct seq_file *part, loff_t *pos) |
284 | { | 306 | { |
285 | loff_t k = *pos; | ||
286 | struct device *dev; | 307 | struct device *dev; |
308 | loff_t n = *pos; | ||
309 | |||
310 | if (!n) | ||
311 | seq_puts(part, "major minor #blocks name\n\n"); | ||
287 | 312 | ||
288 | mutex_lock(&block_class_lock); | 313 | mutex_lock(&block_class_lock); |
289 | list_for_each_entry(dev, &block_class.devices, node) { | 314 | dev = class_find_device(&block_class, NULL, (void *)pos, find_start); |
290 | if (dev->type != &disk_type) | 315 | if (dev) |
291 | continue; | 316 | return dev_to_disk(dev); |
292 | if (!k--) | ||
293 | return dev_to_disk(dev); | ||
294 | } | ||
295 | return NULL; | 317 | return NULL; |
296 | } | 318 | } |
297 | 319 | ||
320 | static int find_next(struct device *dev, void *data) | ||
321 | { | ||
322 | if (dev->type == &disk_type) | ||
323 | return 1; | ||
324 | return 0; | ||
325 | } | ||
326 | |||
298 | static void *part_next(struct seq_file *part, void *v, loff_t *pos) | 327 | static void *part_next(struct seq_file *part, void *v, loff_t *pos) |
299 | { | 328 | { |
300 | struct gendisk *gp = v; | 329 | struct gendisk *gp = v; |
301 | struct device *dev; | 330 | struct device *dev; |
302 | ++*pos; | 331 | ++*pos; |
303 | list_for_each_entry(dev, &gp->dev.node, node) { | 332 | dev = class_find_device(&block_class, &gp->dev, NULL, find_next); |
304 | if (&dev->node == &block_class.devices) | 333 | if (dev) |
305 | return NULL; | 334 | return dev_to_disk(dev); |
306 | if (dev->type == &disk_type) | ||
307 | return dev_to_disk(dev); | ||
308 | } | ||
309 | return NULL; | 335 | return NULL; |
310 | } | 336 | } |
311 | 337 | ||
@@ -320,9 +346,6 @@ static int show_partition(struct seq_file *part, void *v) | |||
320 | int n; | 346 | int n; |
321 | char buf[BDEVNAME_SIZE]; | 347 | char buf[BDEVNAME_SIZE]; |
322 | 348 | ||
323 | if (&sgp->dev.node == block_class.devices.next) | ||
324 | seq_puts(part, "major minor #blocks name\n\n"); | ||
325 | |||
326 | /* Don't show non-partitionable removeable devices or empty devices */ | 349 | /* Don't show non-partitionable removeable devices or empty devices */ |
327 | if (!get_capacity(sgp) || | 350 | if (!get_capacity(sgp) || |
328 | (sgp->minors == 1 && (sgp->flags & GENHD_FL_REMOVABLE))) | 351 | (sgp->minors == 1 && (sgp->flags & GENHD_FL_REMOVABLE))) |
@@ -368,7 +391,10 @@ static struct kobject *base_probe(dev_t devt, int *part, void *data) | |||
368 | 391 | ||
369 | static int __init genhd_device_init(void) | 392 | static int __init genhd_device_init(void) |
370 | { | 393 | { |
371 | int error = class_register(&block_class); | 394 | int error; |
395 | |||
396 | block_class.dev_kobj = sysfs_dev_block_kobj; | ||
397 | error = class_register(&block_class); | ||
372 | if (unlikely(error)) | 398 | if (unlikely(error)) |
373 | return error; | 399 | return error; |
374 | bdev_map = kobj_map_init(base_probe, &block_class_lock); | 400 | bdev_map = kobj_map_init(base_probe, &block_class_lock); |
@@ -400,6 +426,14 @@ static ssize_t disk_removable_show(struct device *dev, | |||
400 | (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0)); | 426 | (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0)); |
401 | } | 427 | } |
402 | 428 | ||
429 | static ssize_t disk_ro_show(struct device *dev, | ||
430 | struct device_attribute *attr, char *buf) | ||
431 | { | ||
432 | struct gendisk *disk = dev_to_disk(dev); | ||
433 | |||
434 | return sprintf(buf, "%d\n", disk->policy ? 1 : 0); | ||
435 | } | ||
436 | |||
403 | static ssize_t disk_size_show(struct device *dev, | 437 | static ssize_t disk_size_show(struct device *dev, |
404 | struct device_attribute *attr, char *buf) | 438 | struct device_attribute *attr, char *buf) |
405 | { | 439 | { |
@@ -472,6 +506,7 @@ static ssize_t disk_fail_store(struct device *dev, | |||
472 | 506 | ||
473 | static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); | 507 | static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); |
474 | static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); | 508 | static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); |
509 | static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL); | ||
475 | static DEVICE_ATTR(size, S_IRUGO, disk_size_show, NULL); | 510 | static DEVICE_ATTR(size, S_IRUGO, disk_size_show, NULL); |
476 | static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL); | 511 | static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL); |
477 | static DEVICE_ATTR(stat, S_IRUGO, disk_stat_show, NULL); | 512 | static DEVICE_ATTR(stat, S_IRUGO, disk_stat_show, NULL); |
@@ -483,6 +518,7 @@ static struct device_attribute dev_attr_fail = | |||
483 | static struct attribute *disk_attrs[] = { | 518 | static struct attribute *disk_attrs[] = { |
484 | &dev_attr_range.attr, | 519 | &dev_attr_range.attr, |
485 | &dev_attr_removable.attr, | 520 | &dev_attr_removable.attr, |
521 | &dev_attr_ro.attr, | ||
486 | &dev_attr_size.attr, | 522 | &dev_attr_size.attr, |
487 | &dev_attr_capability.attr, | 523 | &dev_attr_capability.attr, |
488 | &dev_attr_stat.attr, | 524 | &dev_attr_stat.attr, |
@@ -520,6 +556,7 @@ static struct device_type disk_type = { | |||
520 | .release = disk_release, | 556 | .release = disk_release, |
521 | }; | 557 | }; |
522 | 558 | ||
559 | #ifdef CONFIG_PROC_FS | ||
523 | /* | 560 | /* |
524 | * aggregate disk stat collector. Uses the same stats that the sysfs | 561 | * aggregate disk stat collector. Uses the same stats that the sysfs |
525 | * entries do, above, but makes them available through one seq_file. | 562 | * entries do, above, but makes them available through one seq_file. |
@@ -530,16 +567,12 @@ static struct device_type disk_type = { | |||
530 | 567 | ||
531 | static void *diskstats_start(struct seq_file *part, loff_t *pos) | 568 | static void *diskstats_start(struct seq_file *part, loff_t *pos) |
532 | { | 569 | { |
533 | loff_t k = *pos; | ||
534 | struct device *dev; | 570 | struct device *dev; |
535 | 571 | ||
536 | mutex_lock(&block_class_lock); | 572 | mutex_lock(&block_class_lock); |
537 | list_for_each_entry(dev, &block_class.devices, node) { | 573 | dev = class_find_device(&block_class, NULL, (void *)pos, find_start); |
538 | if (dev->type != &disk_type) | 574 | if (dev) |
539 | continue; | 575 | return dev_to_disk(dev); |
540 | if (!k--) | ||
541 | return dev_to_disk(dev); | ||
542 | } | ||
543 | return NULL; | 576 | return NULL; |
544 | } | 577 | } |
545 | 578 | ||
@@ -549,12 +582,9 @@ static void *diskstats_next(struct seq_file *part, void *v, loff_t *pos) | |||
549 | struct device *dev; | 582 | struct device *dev; |
550 | 583 | ||
551 | ++*pos; | 584 | ++*pos; |
552 | list_for_each_entry(dev, &gp->dev.node, node) { | 585 | dev = class_find_device(&block_class, &gp->dev, NULL, find_next); |
553 | if (&dev->node == &block_class.devices) | 586 | if (dev) |
554 | return NULL; | 587 | return dev_to_disk(dev); |
555 | if (dev->type == &disk_type) | ||
556 | return dev_to_disk(dev); | ||
557 | } | ||
558 | return NULL; | 588 | return NULL; |
559 | } | 589 | } |
560 | 590 | ||
@@ -629,6 +659,7 @@ const struct seq_operations diskstats_op = { | |||
629 | .stop = diskstats_stop, | 659 | .stop = diskstats_stop, |
630 | .show = diskstats_show | 660 | .show = diskstats_show |
631 | }; | 661 | }; |
662 | #endif /* CONFIG_PROC_FS */ | ||
632 | 663 | ||
633 | static void media_change_notify_thread(struct work_struct *work) | 664 | static void media_change_notify_thread(struct work_struct *work) |
634 | { | 665 | { |
@@ -653,24 +684,38 @@ void genhd_media_change_notify(struct gendisk *disk) | |||
653 | EXPORT_SYMBOL_GPL(genhd_media_change_notify); | 684 | EXPORT_SYMBOL_GPL(genhd_media_change_notify); |
654 | #endif /* 0 */ | 685 | #endif /* 0 */ |
655 | 686 | ||
687 | struct find_block { | ||
688 | const char *name; | ||
689 | int part; | ||
690 | }; | ||
691 | |||
692 | static int match_id(struct device *dev, void *data) | ||
693 | { | ||
694 | struct find_block *find = data; | ||
695 | |||
696 | if (dev->type != &disk_type) | ||
697 | return 0; | ||
698 | if (strcmp(dev->bus_id, find->name) == 0) { | ||
699 | struct gendisk *disk = dev_to_disk(dev); | ||
700 | if (find->part < disk->minors) | ||
701 | return 1; | ||
702 | } | ||
703 | return 0; | ||
704 | } | ||
705 | |||
656 | dev_t blk_lookup_devt(const char *name, int part) | 706 | dev_t blk_lookup_devt(const char *name, int part) |
657 | { | 707 | { |
658 | struct device *dev; | 708 | struct device *dev; |
659 | dev_t devt = MKDEV(0, 0); | 709 | dev_t devt = MKDEV(0, 0); |
710 | struct find_block find; | ||
660 | 711 | ||
661 | mutex_lock(&block_class_lock); | 712 | mutex_lock(&block_class_lock); |
662 | list_for_each_entry(dev, &block_class.devices, node) { | 713 | find.name = name; |
663 | if (dev->type != &disk_type) | 714 | find.part = part; |
664 | continue; | 715 | dev = class_find_device(&block_class, NULL, (void *)&find, match_id); |
665 | if (strcmp(dev->bus_id, name) == 0) { | 716 | if (dev) |
666 | struct gendisk *disk = dev_to_disk(dev); | 717 | devt = MKDEV(MAJOR(dev->devt), |
667 | 718 | MINOR(dev->devt) + part); | |
668 | if (part < disk->minors) | ||
669 | devt = MKDEV(MAJOR(dev->devt), | ||
670 | MINOR(dev->devt) + part); | ||
671 | break; | ||
672 | } | ||
673 | } | ||
674 | mutex_unlock(&block_class_lock); | 719 | mutex_unlock(&block_class_lock); |
675 | 720 | ||
676 | return devt; | 721 | return devt; |