diff options
Diffstat (limited to 'block/genhd.c')
-rw-r--r-- | block/genhd.c | 965 |
1 files changed, 634 insertions, 331 deletions
diff --git a/block/genhd.c b/block/genhd.c index e0ce23ac2ece..4cd3433c99ac 100644 --- a/block/genhd.c +++ b/block/genhd.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/kobj_map.h> | 16 | #include <linux/kobj_map.h> |
17 | #include <linux/buffer_head.h> | 17 | #include <linux/buffer_head.h> |
18 | #include <linux/mutex.h> | 18 | #include <linux/mutex.h> |
19 | #include <linux/idr.h> | ||
19 | 20 | ||
20 | #include "blk.h" | 21 | #include "blk.h" |
21 | 22 | ||
@@ -24,8 +25,194 @@ static DEFINE_MUTEX(block_class_lock); | |||
24 | struct kobject *block_depr; | 25 | struct kobject *block_depr; |
25 | #endif | 26 | #endif |
26 | 27 | ||
28 | /* for extended dynamic devt allocation, currently only one major is used */ | ||
29 | #define MAX_EXT_DEVT (1 << MINORBITS) | ||
30 | |||
31 | /* For extended devt allocation. ext_devt_mutex prevents look up | ||
32 | * results from going away underneath its user. | ||
33 | */ | ||
34 | static DEFINE_MUTEX(ext_devt_mutex); | ||
35 | static DEFINE_IDR(ext_devt_idr); | ||
36 | |||
27 | static struct device_type disk_type; | 37 | static struct device_type disk_type; |
28 | 38 | ||
39 | /** | ||
40 | * disk_get_part - get partition | ||
41 | * @disk: disk to look partition from | ||
42 | * @partno: partition number | ||
43 | * | ||
44 | * Look for partition @partno from @disk. If found, increment | ||
45 | * reference count and return it. | ||
46 | * | ||
47 | * CONTEXT: | ||
48 | * Don't care. | ||
49 | * | ||
50 | * RETURNS: | ||
51 | * Pointer to the found partition on success, NULL if not found. | ||
52 | */ | ||
53 | struct hd_struct *disk_get_part(struct gendisk *disk, int partno) | ||
54 | { | ||
55 | struct hd_struct *part = NULL; | ||
56 | struct disk_part_tbl *ptbl; | ||
57 | |||
58 | if (unlikely(partno < 0)) | ||
59 | return NULL; | ||
60 | |||
61 | rcu_read_lock(); | ||
62 | |||
63 | ptbl = rcu_dereference(disk->part_tbl); | ||
64 | if (likely(partno < ptbl->len)) { | ||
65 | part = rcu_dereference(ptbl->part[partno]); | ||
66 | if (part) | ||
67 | get_device(part_to_dev(part)); | ||
68 | } | ||
69 | |||
70 | rcu_read_unlock(); | ||
71 | |||
72 | return part; | ||
73 | } | ||
74 | EXPORT_SYMBOL_GPL(disk_get_part); | ||
75 | |||
76 | /** | ||
77 | * disk_part_iter_init - initialize partition iterator | ||
78 | * @piter: iterator to initialize | ||
79 | * @disk: disk to iterate over | ||
80 | * @flags: DISK_PITER_* flags | ||
81 | * | ||
82 | * Initialize @piter so that it iterates over partitions of @disk. | ||
83 | * | ||
84 | * CONTEXT: | ||
85 | * Don't care. | ||
86 | */ | ||
87 | void disk_part_iter_init(struct disk_part_iter *piter, struct gendisk *disk, | ||
88 | unsigned int flags) | ||
89 | { | ||
90 | struct disk_part_tbl *ptbl; | ||
91 | |||
92 | rcu_read_lock(); | ||
93 | ptbl = rcu_dereference(disk->part_tbl); | ||
94 | |||
95 | piter->disk = disk; | ||
96 | piter->part = NULL; | ||
97 | |||
98 | if (flags & DISK_PITER_REVERSE) | ||
99 | piter->idx = ptbl->len - 1; | ||
100 | else if (flags & DISK_PITER_INCL_PART0) | ||
101 | piter->idx = 0; | ||
102 | else | ||
103 | piter->idx = 1; | ||
104 | |||
105 | piter->flags = flags; | ||
106 | |||
107 | rcu_read_unlock(); | ||
108 | } | ||
109 | EXPORT_SYMBOL_GPL(disk_part_iter_init); | ||
110 | |||
111 | /** | ||
112 | * disk_part_iter_next - proceed iterator to the next partition and return it | ||
113 | * @piter: iterator of interest | ||
114 | * | ||
115 | * Proceed @piter to the next partition and return it. | ||
116 | * | ||
117 | * CONTEXT: | ||
118 | * Don't care. | ||
119 | */ | ||
120 | struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) | ||
121 | { | ||
122 | struct disk_part_tbl *ptbl; | ||
123 | int inc, end; | ||
124 | |||
125 | /* put the last partition */ | ||
126 | disk_put_part(piter->part); | ||
127 | piter->part = NULL; | ||
128 | |||
129 | /* get part_tbl */ | ||
130 | rcu_read_lock(); | ||
131 | ptbl = rcu_dereference(piter->disk->part_tbl); | ||
132 | |||
133 | /* determine iteration parameters */ | ||
134 | if (piter->flags & DISK_PITER_REVERSE) { | ||
135 | inc = -1; | ||
136 | if (piter->flags & DISK_PITER_INCL_PART0) | ||
137 | end = -1; | ||
138 | else | ||
139 | end = 0; | ||
140 | } else { | ||
141 | inc = 1; | ||
142 | end = ptbl->len; | ||
143 | } | ||
144 | |||
145 | /* iterate to the next partition */ | ||
146 | for (; piter->idx != end; piter->idx += inc) { | ||
147 | struct hd_struct *part; | ||
148 | |||
149 | part = rcu_dereference(ptbl->part[piter->idx]); | ||
150 | if (!part) | ||
151 | continue; | ||
152 | if (!(piter->flags & DISK_PITER_INCL_EMPTY) && !part->nr_sects) | ||
153 | continue; | ||
154 | |||
155 | get_device(part_to_dev(part)); | ||
156 | piter->part = part; | ||
157 | piter->idx += inc; | ||
158 | break; | ||
159 | } | ||
160 | |||
161 | rcu_read_unlock(); | ||
162 | |||
163 | return piter->part; | ||
164 | } | ||
165 | EXPORT_SYMBOL_GPL(disk_part_iter_next); | ||
166 | |||
167 | /** | ||
168 | * disk_part_iter_exit - finish up partition iteration | ||
169 | * @piter: iter of interest | ||
170 | * | ||
171 | * Called when iteration is over. Cleans up @piter. | ||
172 | * | ||
173 | * CONTEXT: | ||
174 | * Don't care. | ||
175 | */ | ||
176 | void disk_part_iter_exit(struct disk_part_iter *piter) | ||
177 | { | ||
178 | disk_put_part(piter->part); | ||
179 | piter->part = NULL; | ||
180 | } | ||
181 | EXPORT_SYMBOL_GPL(disk_part_iter_exit); | ||
182 | |||
183 | /** | ||
184 | * disk_map_sector_rcu - map sector to partition | ||
185 | * @disk: gendisk of interest | ||
186 | * @sector: sector to map | ||
187 | * | ||
188 | * Find out which partition @sector maps to on @disk. This is | ||
189 | * primarily used for stats accounting. | ||
190 | * | ||
191 | * CONTEXT: | ||
192 | * RCU read locked. The returned partition pointer is valid only | ||
193 | * while preemption is disabled. | ||
194 | * | ||
195 | * RETURNS: | ||
196 | * Found partition on success, part0 is returned if no partition matches | ||
197 | */ | ||
198 | struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector) | ||
199 | { | ||
200 | struct disk_part_tbl *ptbl; | ||
201 | int i; | ||
202 | |||
203 | ptbl = rcu_dereference(disk->part_tbl); | ||
204 | |||
205 | for (i = 1; i < ptbl->len; i++) { | ||
206 | struct hd_struct *part = rcu_dereference(ptbl->part[i]); | ||
207 | |||
208 | if (part && part->start_sect <= sector && | ||
209 | sector < part->start_sect + part->nr_sects) | ||
210 | return part; | ||
211 | } | ||
212 | return &disk->part0; | ||
213 | } | ||
214 | EXPORT_SYMBOL_GPL(disk_map_sector_rcu); | ||
215 | |||
29 | /* | 216 | /* |
30 | * Can be deleted altogether. Later. | 217 | * Can be deleted altogether. Later. |
31 | * | 218 | * |
@@ -43,14 +230,14 @@ static inline int major_to_index(int major) | |||
43 | } | 230 | } |
44 | 231 | ||
45 | #ifdef CONFIG_PROC_FS | 232 | #ifdef CONFIG_PROC_FS |
46 | void blkdev_show(struct seq_file *f, off_t offset) | 233 | void blkdev_show(struct seq_file *seqf, off_t offset) |
47 | { | 234 | { |
48 | struct blk_major_name *dp; | 235 | struct blk_major_name *dp; |
49 | 236 | ||
50 | if (offset < BLKDEV_MAJOR_HASH_SIZE) { | 237 | if (offset < BLKDEV_MAJOR_HASH_SIZE) { |
51 | mutex_lock(&block_class_lock); | 238 | mutex_lock(&block_class_lock); |
52 | for (dp = major_names[offset]; dp; dp = dp->next) | 239 | for (dp = major_names[offset]; dp; dp = dp->next) |
53 | seq_printf(f, "%3d %s\n", dp->major, dp->name); | 240 | seq_printf(seqf, "%3d %s\n", dp->major, dp->name); |
54 | mutex_unlock(&block_class_lock); | 241 | mutex_unlock(&block_class_lock); |
55 | } | 242 | } |
56 | } | 243 | } |
@@ -136,6 +323,118 @@ EXPORT_SYMBOL(unregister_blkdev); | |||
136 | 323 | ||
137 | static struct kobj_map *bdev_map; | 324 | static struct kobj_map *bdev_map; |
138 | 325 | ||
326 | /** | ||
327 | * blk_mangle_minor - scatter minor numbers apart | ||
328 | * @minor: minor number to mangle | ||
329 | * | ||
330 | * Scatter consecutively allocated @minor number apart if MANGLE_DEVT | ||
331 | * is enabled. Mangling twice gives the original value. | ||
332 | * | ||
333 | * RETURNS: | ||
334 | * Mangled value. | ||
335 | * | ||
336 | * CONTEXT: | ||
337 | * Don't care. | ||
338 | */ | ||
339 | static int blk_mangle_minor(int minor) | ||
340 | { | ||
341 | #ifdef CONFIG_DEBUG_BLOCK_EXT_DEVT | ||
342 | int i; | ||
343 | |||
344 | for (i = 0; i < MINORBITS / 2; i++) { | ||
345 | int low = minor & (1 << i); | ||
346 | int high = minor & (1 << (MINORBITS - 1 - i)); | ||
347 | int distance = MINORBITS - 1 - 2 * i; | ||
348 | |||
349 | minor ^= low | high; /* clear both bits */ | ||
350 | low <<= distance; /* swap the positions */ | ||
351 | high >>= distance; | ||
352 | minor |= low | high; /* and set */ | ||
353 | } | ||
354 | #endif | ||
355 | return minor; | ||
356 | } | ||
357 | |||
358 | /** | ||
359 | * blk_alloc_devt - allocate a dev_t for a partition | ||
360 | * @part: partition to allocate dev_t for | ||
361 | * @gfp_mask: memory allocation flag | ||
362 | * @devt: out parameter for resulting dev_t | ||
363 | * | ||
364 | * Allocate a dev_t for block device. | ||
365 | * | ||
366 | * RETURNS: | ||
367 | * 0 on success, allocated dev_t is returned in *@devt. -errno on | ||
368 | * failure. | ||
369 | * | ||
370 | * CONTEXT: | ||
371 | * Might sleep. | ||
372 | */ | ||
373 | int blk_alloc_devt(struct hd_struct *part, dev_t *devt) | ||
374 | { | ||
375 | struct gendisk *disk = part_to_disk(part); | ||
376 | int idx, rc; | ||
377 | |||
378 | /* in consecutive minor range? */ | ||
379 | if (part->partno < disk->minors) { | ||
380 | *devt = MKDEV(disk->major, disk->first_minor + part->partno); | ||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | /* allocate ext devt */ | ||
385 | do { | ||
386 | if (!idr_pre_get(&ext_devt_idr, GFP_KERNEL)) | ||
387 | return -ENOMEM; | ||
388 | rc = idr_get_new(&ext_devt_idr, part, &idx); | ||
389 | } while (rc == -EAGAIN); | ||
390 | |||
391 | if (rc) | ||
392 | return rc; | ||
393 | |||
394 | if (idx > MAX_EXT_DEVT) { | ||
395 | idr_remove(&ext_devt_idr, idx); | ||
396 | return -EBUSY; | ||
397 | } | ||
398 | |||
399 | *devt = MKDEV(BLOCK_EXT_MAJOR, blk_mangle_minor(idx)); | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | /** | ||
404 | * blk_free_devt - free a dev_t | ||
405 | * @devt: dev_t to free | ||
406 | * | ||
407 | * Free @devt which was allocated using blk_alloc_devt(). | ||
408 | * | ||
409 | * CONTEXT: | ||
410 | * Might sleep. | ||
411 | */ | ||
412 | void blk_free_devt(dev_t devt) | ||
413 | { | ||
414 | might_sleep(); | ||
415 | |||
416 | if (devt == MKDEV(0, 0)) | ||
417 | return; | ||
418 | |||
419 | if (MAJOR(devt) == BLOCK_EXT_MAJOR) { | ||
420 | mutex_lock(&ext_devt_mutex); | ||
421 | idr_remove(&ext_devt_idr, blk_mangle_minor(MINOR(devt))); | ||
422 | mutex_unlock(&ext_devt_mutex); | ||
423 | } | ||
424 | } | ||
425 | |||
426 | static char *bdevt_str(dev_t devt, char *buf) | ||
427 | { | ||
428 | if (MAJOR(devt) <= 0xff && MINOR(devt) <= 0xff) { | ||
429 | char tbuf[BDEVT_SIZE]; | ||
430 | snprintf(tbuf, BDEVT_SIZE, "%02x%02x", MAJOR(devt), MINOR(devt)); | ||
431 | snprintf(buf, BDEVT_SIZE, "%-9s", tbuf); | ||
432 | } else | ||
433 | snprintf(buf, BDEVT_SIZE, "%03x:%05x", MAJOR(devt), MINOR(devt)); | ||
434 | |||
435 | return buf; | ||
436 | } | ||
437 | |||
139 | /* | 438 | /* |
140 | * Register device numbers dev..(dev+range-1) | 439 | * Register device numbers dev..(dev+range-1) |
141 | * range must be nonzero | 440 | * range must be nonzero |
@@ -157,11 +456,11 @@ void blk_unregister_region(dev_t devt, unsigned long range) | |||
157 | 456 | ||
158 | EXPORT_SYMBOL(blk_unregister_region); | 457 | EXPORT_SYMBOL(blk_unregister_region); |
159 | 458 | ||
160 | static struct kobject *exact_match(dev_t devt, int *part, void *data) | 459 | static struct kobject *exact_match(dev_t devt, int *partno, void *data) |
161 | { | 460 | { |
162 | struct gendisk *p = data; | 461 | struct gendisk *p = data; |
163 | 462 | ||
164 | return &p->dev.kobj; | 463 | return &disk_to_dev(p)->kobj; |
165 | } | 464 | } |
166 | 465 | ||
167 | static int exact_lock(dev_t devt, void *data) | 466 | static int exact_lock(dev_t devt, void *data) |
@@ -179,21 +478,46 @@ static int exact_lock(dev_t devt, void *data) | |||
179 | * | 478 | * |
180 | * This function registers the partitioning information in @disk | 479 | * This function registers the partitioning information in @disk |
181 | * with the kernel. | 480 | * with the kernel. |
481 | * | ||
482 | * FIXME: error handling | ||
182 | */ | 483 | */ |
183 | void add_disk(struct gendisk *disk) | 484 | void add_disk(struct gendisk *disk) |
184 | { | 485 | { |
185 | struct backing_dev_info *bdi; | 486 | struct backing_dev_info *bdi; |
487 | dev_t devt; | ||
186 | int retval; | 488 | int retval; |
187 | 489 | ||
490 | /* minors == 0 indicates to use ext devt from part0 and should | ||
491 | * be accompanied with EXT_DEVT flag. Make sure all | ||
492 | * parameters make sense. | ||
493 | */ | ||
494 | WARN_ON(disk->minors && !(disk->major || disk->first_minor)); | ||
495 | WARN_ON(!disk->minors && !(disk->flags & GENHD_FL_EXT_DEVT)); | ||
496 | |||
188 | disk->flags |= GENHD_FL_UP; | 497 | disk->flags |= GENHD_FL_UP; |
189 | blk_register_region(MKDEV(disk->major, disk->first_minor), | 498 | |
190 | disk->minors, NULL, exact_match, exact_lock, disk); | 499 | retval = blk_alloc_devt(&disk->part0, &devt); |
500 | if (retval) { | ||
501 | WARN_ON(1); | ||
502 | return; | ||
503 | } | ||
504 | disk_to_dev(disk)->devt = devt; | ||
505 | |||
506 | /* ->major and ->first_minor aren't supposed to be | ||
507 | * dereferenced from here on, but set them just in case. | ||
508 | */ | ||
509 | disk->major = MAJOR(devt); | ||
510 | disk->first_minor = MINOR(devt); | ||
511 | |||
512 | blk_register_region(disk_devt(disk), disk->minors, NULL, | ||
513 | exact_match, exact_lock, disk); | ||
191 | register_disk(disk); | 514 | register_disk(disk); |
192 | blk_register_queue(disk); | 515 | blk_register_queue(disk); |
193 | 516 | ||
194 | bdi = &disk->queue->backing_dev_info; | 517 | bdi = &disk->queue->backing_dev_info; |
195 | bdi_register_dev(bdi, MKDEV(disk->major, disk->first_minor)); | 518 | bdi_register_dev(bdi, disk_devt(disk)); |
196 | retval = sysfs_create_link(&disk->dev.kobj, &bdi->dev->kobj, "bdi"); | 519 | retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj, |
520 | "bdi"); | ||
197 | WARN_ON(retval); | 521 | WARN_ON(retval); |
198 | } | 522 | } |
199 | 523 | ||
@@ -202,78 +526,71 @@ EXPORT_SYMBOL(del_gendisk); /* in partitions/check.c */ | |||
202 | 526 | ||
203 | void unlink_gendisk(struct gendisk *disk) | 527 | void unlink_gendisk(struct gendisk *disk) |
204 | { | 528 | { |
205 | sysfs_remove_link(&disk->dev.kobj, "bdi"); | 529 | sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi"); |
206 | bdi_unregister(&disk->queue->backing_dev_info); | 530 | bdi_unregister(&disk->queue->backing_dev_info); |
207 | blk_unregister_queue(disk); | 531 | blk_unregister_queue(disk); |
208 | blk_unregister_region(MKDEV(disk->major, disk->first_minor), | 532 | blk_unregister_region(disk_devt(disk), disk->minors); |
209 | disk->minors); | ||
210 | } | 533 | } |
211 | 534 | ||
212 | /** | 535 | /** |
213 | * get_gendisk - get partitioning information for a given device | 536 | * get_gendisk - get partitioning information for a given device |
214 | * @dev: device to get partitioning information for | 537 | * @devt: device to get partitioning information for |
538 | * @part: returned partition index | ||
215 | * | 539 | * |
216 | * This function gets the structure containing partitioning | 540 | * This function gets the structure containing partitioning |
217 | * information for the given device @dev. | 541 | * information for the given device @devt. |
218 | */ | 542 | */ |
219 | struct gendisk *get_gendisk(dev_t devt, int *part) | 543 | struct gendisk *get_gendisk(dev_t devt, int *partno) |
220 | { | 544 | { |
221 | struct kobject *kobj = kobj_lookup(bdev_map, devt, part); | 545 | struct gendisk *disk = NULL; |
222 | struct device *dev = kobj_to_dev(kobj); | 546 | |
547 | if (MAJOR(devt) != BLOCK_EXT_MAJOR) { | ||
548 | struct kobject *kobj; | ||
549 | |||
550 | kobj = kobj_lookup(bdev_map, devt, partno); | ||
551 | if (kobj) | ||
552 | disk = dev_to_disk(kobj_to_dev(kobj)); | ||
553 | } else { | ||
554 | struct hd_struct *part; | ||
223 | 555 | ||
224 | return kobj ? dev_to_disk(dev) : NULL; | 556 | mutex_lock(&ext_devt_mutex); |
557 | part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt))); | ||
558 | if (part && get_disk(part_to_disk(part))) { | ||
559 | *partno = part->partno; | ||
560 | disk = part_to_disk(part); | ||
561 | } | ||
562 | mutex_unlock(&ext_devt_mutex); | ||
563 | } | ||
564 | |||
565 | return disk; | ||
225 | } | 566 | } |
226 | 567 | ||
227 | /* | 568 | /** |
228 | * print a partitions - intended for places where the root filesystem can't be | 569 | * bdget_disk - do bdget() by gendisk and partition number |
229 | * mounted and thus to give the victim some idea of what went wrong | 570 | * @disk: gendisk of interest |
571 | * @partno: partition number | ||
572 | * | ||
573 | * Find partition @partno from @disk, do bdget() on it. | ||
574 | * | ||
575 | * CONTEXT: | ||
576 | * Don't care. | ||
577 | * | ||
578 | * RETURNS: | ||
579 | * Resulting block_device on success, NULL on failure. | ||
230 | */ | 580 | */ |
231 | static int printk_partition(struct device *dev, void *data) | 581 | struct block_device *bdget_disk(struct gendisk *disk, int partno) |
232 | { | 582 | { |
233 | struct gendisk *sgp; | 583 | struct hd_struct *part; |
234 | char buf[BDEVNAME_SIZE]; | 584 | struct block_device *bdev = NULL; |
235 | int n; | ||
236 | |||
237 | if (dev->type != &disk_type) | ||
238 | goto exit; | ||
239 | 585 | ||
240 | sgp = dev_to_disk(dev); | 586 | part = disk_get_part(disk, partno); |
241 | /* | 587 | if (part) |
242 | * Don't show empty devices or things that have been surpressed | 588 | bdev = bdget(part_devt(part)); |
243 | */ | 589 | disk_put_part(part); |
244 | if (get_capacity(sgp) == 0 || | ||
245 | (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO)) | ||
246 | goto exit; | ||
247 | 590 | ||
248 | /* | 591 | return bdev; |
249 | * Note, unlike /proc/partitions, I am showing the numbers in | ||
250 | * hex - the same format as the root= option takes. | ||
251 | */ | ||
252 | printk("%02x%02x %10llu %s", | ||
253 | sgp->major, sgp->first_minor, | ||
254 | (unsigned long long)get_capacity(sgp) >> 1, | ||
255 | disk_name(sgp, 0, buf)); | ||
256 | if (sgp->driverfs_dev != NULL && | ||
257 | sgp->driverfs_dev->driver != NULL) | ||
258 | printk(" driver: %s\n", | ||
259 | sgp->driverfs_dev->driver->name); | ||
260 | else | ||
261 | printk(" (driver?)\n"); | ||
262 | |||
263 | /* now show the partitions */ | ||
264 | for (n = 0; n < sgp->minors - 1; ++n) { | ||
265 | if (sgp->part[n] == NULL) | ||
266 | goto exit; | ||
267 | if (sgp->part[n]->nr_sects == 0) | ||
268 | goto exit; | ||
269 | printk(" %02x%02x %10llu %s\n", | ||
270 | sgp->major, n + 1 + sgp->first_minor, | ||
271 | (unsigned long long)sgp->part[n]->nr_sects >> 1, | ||
272 | disk_name(sgp, n + 1, buf)); | ||
273 | } | ||
274 | exit: | ||
275 | return 0; | ||
276 | } | 592 | } |
593 | EXPORT_SYMBOL(bdget_disk); | ||
277 | 594 | ||
278 | /* | 595 | /* |
279 | * print a full list of all partitions - intended for places where the root | 596 | * print a full list of all partitions - intended for places where the root |
@@ -282,120 +599,145 @@ exit: | |||
282 | */ | 599 | */ |
283 | void __init printk_all_partitions(void) | 600 | void __init printk_all_partitions(void) |
284 | { | 601 | { |
285 | mutex_lock(&block_class_lock); | 602 | struct class_dev_iter iter; |
286 | class_for_each_device(&block_class, NULL, NULL, printk_partition); | 603 | struct device *dev; |
287 | mutex_unlock(&block_class_lock); | 604 | |
605 | class_dev_iter_init(&iter, &block_class, NULL, &disk_type); | ||
606 | while ((dev = class_dev_iter_next(&iter))) { | ||
607 | struct gendisk *disk = dev_to_disk(dev); | ||
608 | struct disk_part_iter piter; | ||
609 | struct hd_struct *part; | ||
610 | char name_buf[BDEVNAME_SIZE]; | ||
611 | char devt_buf[BDEVT_SIZE]; | ||
612 | |||
613 | /* | ||
614 | * Don't show empty devices or things that have been | ||
615 | * surpressed | ||
616 | */ | ||
617 | if (get_capacity(disk) == 0 || | ||
618 | (disk->flags & GENHD_FL_SUPPRESS_PARTITION_INFO)) | ||
619 | continue; | ||
620 | |||
621 | /* | ||
622 | * Note, unlike /proc/partitions, I am showing the | ||
623 | * numbers in hex - the same format as the root= | ||
624 | * option takes. | ||
625 | */ | ||
626 | disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0); | ||
627 | while ((part = disk_part_iter_next(&piter))) { | ||
628 | bool is_part0 = part == &disk->part0; | ||
629 | |||
630 | printk("%s%s %10llu %s", is_part0 ? "" : " ", | ||
631 | bdevt_str(part_devt(part), devt_buf), | ||
632 | (unsigned long long)part->nr_sects >> 1, | ||
633 | disk_name(disk, part->partno, name_buf)); | ||
634 | if (is_part0) { | ||
635 | if (disk->driverfs_dev != NULL && | ||
636 | disk->driverfs_dev->driver != NULL) | ||
637 | printk(" driver: %s\n", | ||
638 | disk->driverfs_dev->driver->name); | ||
639 | else | ||
640 | printk(" (driver?)\n"); | ||
641 | } else | ||
642 | printk("\n"); | ||
643 | } | ||
644 | disk_part_iter_exit(&piter); | ||
645 | } | ||
646 | class_dev_iter_exit(&iter); | ||
288 | } | 647 | } |
289 | 648 | ||
290 | #ifdef CONFIG_PROC_FS | 649 | #ifdef CONFIG_PROC_FS |
291 | /* iterator */ | 650 | /* iterator */ |
292 | static int find_start(struct device *dev, void *data) | 651 | static void *disk_seqf_start(struct seq_file *seqf, loff_t *pos) |
293 | { | 652 | { |
294 | loff_t *k = data; | 653 | loff_t skip = *pos; |
654 | struct class_dev_iter *iter; | ||
655 | struct device *dev; | ||
295 | 656 | ||
296 | if (dev->type != &disk_type) | 657 | iter = kmalloc(sizeof(*iter), GFP_KERNEL); |
297 | return 0; | 658 | if (!iter) |
298 | if (!*k) | 659 | return ERR_PTR(-ENOMEM); |
299 | return 1; | 660 | |
300 | (*k)--; | 661 | seqf->private = iter; |
301 | return 0; | 662 | class_dev_iter_init(iter, &block_class, NULL, &disk_type); |
663 | do { | ||
664 | dev = class_dev_iter_next(iter); | ||
665 | if (!dev) | ||
666 | return NULL; | ||
667 | } while (skip--); | ||
668 | |||
669 | return dev_to_disk(dev); | ||
302 | } | 670 | } |
303 | 671 | ||
304 | static void *part_start(struct seq_file *part, loff_t *pos) | 672 | static void *disk_seqf_next(struct seq_file *seqf, void *v, loff_t *pos) |
305 | { | 673 | { |
306 | struct device *dev; | 674 | struct device *dev; |
307 | loff_t k = *pos; | ||
308 | |||
309 | if (!k) | ||
310 | part->private = (void *)1LU; /* tell show to print header */ | ||
311 | 675 | ||
312 | mutex_lock(&block_class_lock); | 676 | (*pos)++; |
313 | dev = class_find_device(&block_class, NULL, &k, find_start); | 677 | dev = class_dev_iter_next(seqf->private); |
314 | if (dev) { | 678 | if (dev) |
315 | put_device(dev); | ||
316 | return dev_to_disk(dev); | 679 | return dev_to_disk(dev); |
317 | } | 680 | |
318 | return NULL; | 681 | return NULL; |
319 | } | 682 | } |
320 | 683 | ||
321 | static int find_next(struct device *dev, void *data) | 684 | static void disk_seqf_stop(struct seq_file *seqf, void *v) |
322 | { | 685 | { |
323 | if (dev->type == &disk_type) | 686 | struct class_dev_iter *iter = seqf->private; |
324 | return 1; | ||
325 | return 0; | ||
326 | } | ||
327 | 687 | ||
328 | static void *part_next(struct seq_file *part, void *v, loff_t *pos) | 688 | /* stop is called even after start failed :-( */ |
329 | { | 689 | if (iter) { |
330 | struct gendisk *gp = v; | 690 | class_dev_iter_exit(iter); |
331 | struct device *dev; | 691 | kfree(iter); |
332 | ++*pos; | ||
333 | dev = class_find_device(&block_class, &gp->dev, NULL, find_next); | ||
334 | if (dev) { | ||
335 | put_device(dev); | ||
336 | return dev_to_disk(dev); | ||
337 | } | 692 | } |
338 | return NULL; | ||
339 | } | 693 | } |
340 | 694 | ||
341 | static void part_stop(struct seq_file *part, void *v) | 695 | static void *show_partition_start(struct seq_file *seqf, loff_t *pos) |
342 | { | 696 | { |
343 | mutex_unlock(&block_class_lock); | 697 | static void *p; |
698 | |||
699 | p = disk_seqf_start(seqf, pos); | ||
700 | if (!IS_ERR(p) && p && !*pos) | ||
701 | seq_puts(seqf, "major minor #blocks name\n\n"); | ||
702 | return p; | ||
344 | } | 703 | } |
345 | 704 | ||
346 | static int show_partition(struct seq_file *part, void *v) | 705 | static int show_partition(struct seq_file *seqf, void *v) |
347 | { | 706 | { |
348 | struct gendisk *sgp = v; | 707 | struct gendisk *sgp = v; |
349 | int n; | 708 | struct disk_part_iter piter; |
709 | struct hd_struct *part; | ||
350 | char buf[BDEVNAME_SIZE]; | 710 | char buf[BDEVNAME_SIZE]; |
351 | 711 | ||
352 | /* | ||
353 | * Print header if start told us to do. This is to preserve | ||
354 | * the original behavior of not printing header if no | ||
355 | * partition exists. This hackery will be removed later with | ||
356 | * class iteration clean up. | ||
357 | */ | ||
358 | if (part->private) { | ||
359 | seq_puts(part, "major minor #blocks name\n\n"); | ||
360 | part->private = NULL; | ||
361 | } | ||
362 | |||
363 | /* Don't show non-partitionable removeable devices or empty devices */ | 712 | /* Don't show non-partitionable removeable devices or empty devices */ |
364 | if (!get_capacity(sgp) || | 713 | if (!get_capacity(sgp) || (!disk_partitionable(sgp) && |
365 | (sgp->minors == 1 && (sgp->flags & GENHD_FL_REMOVABLE))) | 714 | (sgp->flags & GENHD_FL_REMOVABLE))) |
366 | return 0; | 715 | return 0; |
367 | if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO) | 716 | if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO) |
368 | return 0; | 717 | return 0; |
369 | 718 | ||
370 | /* show the full disk and all non-0 size partitions of it */ | 719 | /* show the full disk and all non-0 size partitions of it */ |
371 | seq_printf(part, "%4d %4d %10llu %s\n", | 720 | disk_part_iter_init(&piter, sgp, DISK_PITER_INCL_PART0); |
372 | sgp->major, sgp->first_minor, | 721 | while ((part = disk_part_iter_next(&piter))) |
373 | (unsigned long long)get_capacity(sgp) >> 1, | 722 | seq_printf(seqf, "%4d %7d %10llu %s\n", |
374 | disk_name(sgp, 0, buf)); | 723 | MAJOR(part_devt(part)), MINOR(part_devt(part)), |
375 | for (n = 0; n < sgp->minors - 1; n++) { | 724 | (unsigned long long)part->nr_sects >> 1, |
376 | if (!sgp->part[n]) | 725 | disk_name(sgp, part->partno, buf)); |
377 | continue; | 726 | disk_part_iter_exit(&piter); |
378 | if (sgp->part[n]->nr_sects == 0) | ||
379 | continue; | ||
380 | seq_printf(part, "%4d %4d %10llu %s\n", | ||
381 | sgp->major, n + 1 + sgp->first_minor, | ||
382 | (unsigned long long)sgp->part[n]->nr_sects >> 1 , | ||
383 | disk_name(sgp, n + 1, buf)); | ||
384 | } | ||
385 | 727 | ||
386 | return 0; | 728 | return 0; |
387 | } | 729 | } |
388 | 730 | ||
389 | const struct seq_operations partitions_op = { | 731 | const struct seq_operations partitions_op = { |
390 | .start = part_start, | 732 | .start = show_partition_start, |
391 | .next = part_next, | 733 | .next = disk_seqf_next, |
392 | .stop = part_stop, | 734 | .stop = disk_seqf_stop, |
393 | .show = show_partition | 735 | .show = show_partition |
394 | }; | 736 | }; |
395 | #endif | 737 | #endif |
396 | 738 | ||
397 | 739 | ||
398 | static struct kobject *base_probe(dev_t devt, int *part, void *data) | 740 | static struct kobject *base_probe(dev_t devt, int *partno, void *data) |
399 | { | 741 | { |
400 | if (request_module("block-major-%d-%d", MAJOR(devt), MINOR(devt)) > 0) | 742 | if (request_module("block-major-%d-%d", MAJOR(devt), MINOR(devt)) > 0) |
401 | /* Make old-style 2.4 aliases work */ | 743 | /* Make old-style 2.4 aliases work */ |
@@ -431,29 +773,29 @@ static ssize_t disk_range_show(struct device *dev, | |||
431 | return sprintf(buf, "%d\n", disk->minors); | 773 | return sprintf(buf, "%d\n", disk->minors); |
432 | } | 774 | } |
433 | 775 | ||
434 | static ssize_t disk_removable_show(struct device *dev, | 776 | static ssize_t disk_ext_range_show(struct device *dev, |
435 | struct device_attribute *attr, char *buf) | 777 | struct device_attribute *attr, char *buf) |
436 | { | 778 | { |
437 | struct gendisk *disk = dev_to_disk(dev); | 779 | struct gendisk *disk = dev_to_disk(dev); |
438 | 780 | ||
439 | return sprintf(buf, "%d\n", | 781 | return sprintf(buf, "%d\n", disk_max_parts(disk)); |
440 | (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0)); | ||
441 | } | 782 | } |
442 | 783 | ||
443 | static ssize_t disk_ro_show(struct device *dev, | 784 | static ssize_t disk_removable_show(struct device *dev, |
444 | struct device_attribute *attr, char *buf) | 785 | struct device_attribute *attr, char *buf) |
445 | { | 786 | { |
446 | struct gendisk *disk = dev_to_disk(dev); | 787 | struct gendisk *disk = dev_to_disk(dev); |
447 | 788 | ||
448 | return sprintf(buf, "%d\n", disk->policy ? 1 : 0); | 789 | return sprintf(buf, "%d\n", |
790 | (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0)); | ||
449 | } | 791 | } |
450 | 792 | ||
451 | static ssize_t disk_size_show(struct device *dev, | 793 | static ssize_t disk_ro_show(struct device *dev, |
452 | struct device_attribute *attr, char *buf) | 794 | struct device_attribute *attr, char *buf) |
453 | { | 795 | { |
454 | struct gendisk *disk = dev_to_disk(dev); | 796 | struct gendisk *disk = dev_to_disk(dev); |
455 | 797 | ||
456 | return sprintf(buf, "%llu\n", (unsigned long long)get_capacity(disk)); | 798 | return sprintf(buf, "%d\n", get_disk_ro(disk) ? 1 : 0); |
457 | } | 799 | } |
458 | 800 | ||
459 | static ssize_t disk_capability_show(struct device *dev, | 801 | static ssize_t disk_capability_show(struct device *dev, |
@@ -464,73 +806,26 @@ static ssize_t disk_capability_show(struct device *dev, | |||
464 | return sprintf(buf, "%x\n", disk->flags); | 806 | return sprintf(buf, "%x\n", disk->flags); |
465 | } | 807 | } |
466 | 808 | ||
467 | static ssize_t disk_stat_show(struct device *dev, | ||
468 | struct device_attribute *attr, char *buf) | ||
469 | { | ||
470 | struct gendisk *disk = dev_to_disk(dev); | ||
471 | |||
472 | preempt_disable(); | ||
473 | disk_round_stats(disk); | ||
474 | preempt_enable(); | ||
475 | return sprintf(buf, | ||
476 | "%8lu %8lu %8llu %8u " | ||
477 | "%8lu %8lu %8llu %8u " | ||
478 | "%8u %8u %8u" | ||
479 | "\n", | ||
480 | disk_stat_read(disk, ios[READ]), | ||
481 | disk_stat_read(disk, merges[READ]), | ||
482 | (unsigned long long)disk_stat_read(disk, sectors[READ]), | ||
483 | jiffies_to_msecs(disk_stat_read(disk, ticks[READ])), | ||
484 | disk_stat_read(disk, ios[WRITE]), | ||
485 | disk_stat_read(disk, merges[WRITE]), | ||
486 | (unsigned long long)disk_stat_read(disk, sectors[WRITE]), | ||
487 | jiffies_to_msecs(disk_stat_read(disk, ticks[WRITE])), | ||
488 | disk->in_flight, | ||
489 | jiffies_to_msecs(disk_stat_read(disk, io_ticks)), | ||
490 | jiffies_to_msecs(disk_stat_read(disk, time_in_queue))); | ||
491 | } | ||
492 | |||
493 | #ifdef CONFIG_FAIL_MAKE_REQUEST | ||
494 | static ssize_t disk_fail_show(struct device *dev, | ||
495 | struct device_attribute *attr, char *buf) | ||
496 | { | ||
497 | struct gendisk *disk = dev_to_disk(dev); | ||
498 | |||
499 | return sprintf(buf, "%d\n", disk->flags & GENHD_FL_FAIL ? 1 : 0); | ||
500 | } | ||
501 | |||
502 | static ssize_t disk_fail_store(struct device *dev, | ||
503 | struct device_attribute *attr, | ||
504 | const char *buf, size_t count) | ||
505 | { | ||
506 | struct gendisk *disk = dev_to_disk(dev); | ||
507 | int i; | ||
508 | |||
509 | if (count > 0 && sscanf(buf, "%d", &i) > 0) { | ||
510 | if (i == 0) | ||
511 | disk->flags &= ~GENHD_FL_FAIL; | ||
512 | else | ||
513 | disk->flags |= GENHD_FL_FAIL; | ||
514 | } | ||
515 | |||
516 | return count; | ||
517 | } | ||
518 | |||
519 | #endif | ||
520 | |||
521 | static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); | 809 | static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); |
810 | static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL); | ||
522 | static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); | 811 | static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); |
523 | static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL); | 812 | static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL); |
524 | static DEVICE_ATTR(size, S_IRUGO, disk_size_show, NULL); | 813 | static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL); |
525 | static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL); | 814 | static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL); |
526 | static DEVICE_ATTR(stat, S_IRUGO, disk_stat_show, NULL); | 815 | static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL); |
527 | #ifdef CONFIG_FAIL_MAKE_REQUEST | 816 | #ifdef CONFIG_FAIL_MAKE_REQUEST |
528 | static struct device_attribute dev_attr_fail = | 817 | static struct device_attribute dev_attr_fail = |
529 | __ATTR(make-it-fail, S_IRUGO|S_IWUSR, disk_fail_show, disk_fail_store); | 818 | __ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store); |
819 | #endif | ||
820 | #ifdef CONFIG_FAIL_IO_TIMEOUT | ||
821 | static struct device_attribute dev_attr_fail_timeout = | ||
822 | __ATTR(io-timeout-fail, S_IRUGO|S_IWUSR, part_timeout_show, | ||
823 | part_timeout_store); | ||
530 | #endif | 824 | #endif |
531 | 825 | ||
532 | static struct attribute *disk_attrs[] = { | 826 | static struct attribute *disk_attrs[] = { |
533 | &dev_attr_range.attr, | 827 | &dev_attr_range.attr, |
828 | &dev_attr_ext_range.attr, | ||
534 | &dev_attr_removable.attr, | 829 | &dev_attr_removable.attr, |
535 | &dev_attr_ro.attr, | 830 | &dev_attr_ro.attr, |
536 | &dev_attr_size.attr, | 831 | &dev_attr_size.attr, |
@@ -539,6 +834,9 @@ static struct attribute *disk_attrs[] = { | |||
539 | #ifdef CONFIG_FAIL_MAKE_REQUEST | 834 | #ifdef CONFIG_FAIL_MAKE_REQUEST |
540 | &dev_attr_fail.attr, | 835 | &dev_attr_fail.attr, |
541 | #endif | 836 | #endif |
837 | #ifdef CONFIG_FAIL_IO_TIMEOUT | ||
838 | &dev_attr_fail_timeout.attr, | ||
839 | #endif | ||
542 | NULL | 840 | NULL |
543 | }; | 841 | }; |
544 | 842 | ||
@@ -551,13 +849,87 @@ static struct attribute_group *disk_attr_groups[] = { | |||
551 | NULL | 849 | NULL |
552 | }; | 850 | }; |
553 | 851 | ||
852 | static void disk_free_ptbl_rcu_cb(struct rcu_head *head) | ||
853 | { | ||
854 | struct disk_part_tbl *ptbl = | ||
855 | container_of(head, struct disk_part_tbl, rcu_head); | ||
856 | |||
857 | kfree(ptbl); | ||
858 | } | ||
859 | |||
860 | /** | ||
861 | * disk_replace_part_tbl - replace disk->part_tbl in RCU-safe way | ||
862 | * @disk: disk to replace part_tbl for | ||
863 | * @new_ptbl: new part_tbl to install | ||
864 | * | ||
865 | * Replace disk->part_tbl with @new_ptbl in RCU-safe way. The | ||
866 | * original ptbl is freed using RCU callback. | ||
867 | * | ||
868 | * LOCKING: | ||
869 | * Matching bd_mutx locked. | ||
870 | */ | ||
871 | static void disk_replace_part_tbl(struct gendisk *disk, | ||
872 | struct disk_part_tbl *new_ptbl) | ||
873 | { | ||
874 | struct disk_part_tbl *old_ptbl = disk->part_tbl; | ||
875 | |||
876 | rcu_assign_pointer(disk->part_tbl, new_ptbl); | ||
877 | if (old_ptbl) | ||
878 | call_rcu(&old_ptbl->rcu_head, disk_free_ptbl_rcu_cb); | ||
879 | } | ||
880 | |||
881 | /** | ||
882 | * disk_expand_part_tbl - expand disk->part_tbl | ||
883 | * @disk: disk to expand part_tbl for | ||
884 | * @partno: expand such that this partno can fit in | ||
885 | * | ||
886 | * Expand disk->part_tbl such that @partno can fit in. disk->part_tbl | ||
887 | * uses RCU to allow unlocked dereferencing for stats and other stuff. | ||
888 | * | ||
889 | * LOCKING: | ||
890 | * Matching bd_mutex locked, might sleep. | ||
891 | * | ||
892 | * RETURNS: | ||
893 | * 0 on success, -errno on failure. | ||
894 | */ | ||
895 | int disk_expand_part_tbl(struct gendisk *disk, int partno) | ||
896 | { | ||
897 | struct disk_part_tbl *old_ptbl = disk->part_tbl; | ||
898 | struct disk_part_tbl *new_ptbl; | ||
899 | int len = old_ptbl ? old_ptbl->len : 0; | ||
900 | int target = partno + 1; | ||
901 | size_t size; | ||
902 | int i; | ||
903 | |||
904 | /* disk_max_parts() is zero during initialization, ignore if so */ | ||
905 | if (disk_max_parts(disk) && target > disk_max_parts(disk)) | ||
906 | return -EINVAL; | ||
907 | |||
908 | if (target <= len) | ||
909 | return 0; | ||
910 | |||
911 | size = sizeof(*new_ptbl) + target * sizeof(new_ptbl->part[0]); | ||
912 | new_ptbl = kzalloc_node(size, GFP_KERNEL, disk->node_id); | ||
913 | if (!new_ptbl) | ||
914 | return -ENOMEM; | ||
915 | |||
916 | INIT_RCU_HEAD(&new_ptbl->rcu_head); | ||
917 | new_ptbl->len = target; | ||
918 | |||
919 | for (i = 0; i < len; i++) | ||
920 | rcu_assign_pointer(new_ptbl->part[i], old_ptbl->part[i]); | ||
921 | |||
922 | disk_replace_part_tbl(disk, new_ptbl); | ||
923 | return 0; | ||
924 | } | ||
925 | |||
554 | static void disk_release(struct device *dev) | 926 | static void disk_release(struct device *dev) |
555 | { | 927 | { |
556 | struct gendisk *disk = dev_to_disk(dev); | 928 | struct gendisk *disk = dev_to_disk(dev); |
557 | 929 | ||
558 | kfree(disk->random); | 930 | kfree(disk->random); |
559 | kfree(disk->part); | 931 | disk_replace_part_tbl(disk, NULL); |
560 | free_disk_stats(disk); | 932 | free_part_stats(&disk->part0); |
561 | kfree(disk); | 933 | kfree(disk); |
562 | } | 934 | } |
563 | struct class block_class = { | 935 | struct class block_class = { |
@@ -578,83 +950,31 @@ static struct device_type disk_type = { | |||
578 | * The output looks suspiciously like /proc/partitions with a bunch of | 950 | * The output looks suspiciously like /proc/partitions with a bunch of |
579 | * extra fields. | 951 | * extra fields. |
580 | */ | 952 | */ |
581 | 953 | static int diskstats_show(struct seq_file *seqf, void *v) | |
582 | static void *diskstats_start(struct seq_file *part, loff_t *pos) | ||
583 | { | ||
584 | struct device *dev; | ||
585 | loff_t k = *pos; | ||
586 | |||
587 | mutex_lock(&block_class_lock); | ||
588 | dev = class_find_device(&block_class, NULL, &k, find_start); | ||
589 | if (dev) { | ||
590 | put_device(dev); | ||
591 | return dev_to_disk(dev); | ||
592 | } | ||
593 | return NULL; | ||
594 | } | ||
595 | |||
596 | static void *diskstats_next(struct seq_file *part, void *v, loff_t *pos) | ||
597 | { | ||
598 | struct gendisk *gp = v; | ||
599 | struct device *dev; | ||
600 | |||
601 | ++*pos; | ||
602 | dev = class_find_device(&block_class, &gp->dev, NULL, find_next); | ||
603 | if (dev) { | ||
604 | put_device(dev); | ||
605 | return dev_to_disk(dev); | ||
606 | } | ||
607 | return NULL; | ||
608 | } | ||
609 | |||
610 | static void diskstats_stop(struct seq_file *part, void *v) | ||
611 | { | ||
612 | mutex_unlock(&block_class_lock); | ||
613 | } | ||
614 | |||
615 | static int diskstats_show(struct seq_file *s, void *v) | ||
616 | { | 954 | { |
617 | struct gendisk *gp = v; | 955 | struct gendisk *gp = v; |
956 | struct disk_part_iter piter; | ||
957 | struct hd_struct *hd; | ||
618 | char buf[BDEVNAME_SIZE]; | 958 | char buf[BDEVNAME_SIZE]; |
619 | int n = 0; | 959 | int cpu; |
620 | 960 | ||
621 | /* | 961 | /* |
622 | if (&gp->dev.kobj.entry == block_class.devices.next) | 962 | if (&disk_to_dev(gp)->kobj.entry == block_class.devices.next) |
623 | seq_puts(s, "major minor name" | 963 | seq_puts(seqf, "major minor name" |
624 | " rio rmerge rsect ruse wio wmerge " | 964 | " rio rmerge rsect ruse wio wmerge " |
625 | "wsect wuse running use aveq" | 965 | "wsect wuse running use aveq" |
626 | "\n\n"); | 966 | "\n\n"); |
627 | */ | 967 | */ |
628 | 968 | ||
629 | preempt_disable(); | 969 | disk_part_iter_init(&piter, gp, DISK_PITER_INCL_PART0); |
630 | disk_round_stats(gp); | 970 | while ((hd = disk_part_iter_next(&piter))) { |
631 | preempt_enable(); | 971 | cpu = part_stat_lock(); |
632 | seq_printf(s, "%4d %4d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n", | 972 | part_round_stats(cpu, hd); |
633 | gp->major, n + gp->first_minor, disk_name(gp, n, buf), | 973 | part_stat_unlock(); |
634 | disk_stat_read(gp, ios[0]), disk_stat_read(gp, merges[0]), | 974 | seq_printf(seqf, "%4d %7d %s %lu %lu %llu " |
635 | (unsigned long long)disk_stat_read(gp, sectors[0]), | ||
636 | jiffies_to_msecs(disk_stat_read(gp, ticks[0])), | ||
637 | disk_stat_read(gp, ios[1]), disk_stat_read(gp, merges[1]), | ||
638 | (unsigned long long)disk_stat_read(gp, sectors[1]), | ||
639 | jiffies_to_msecs(disk_stat_read(gp, ticks[1])), | ||
640 | gp->in_flight, | ||
641 | jiffies_to_msecs(disk_stat_read(gp, io_ticks)), | ||
642 | jiffies_to_msecs(disk_stat_read(gp, time_in_queue))); | ||
643 | |||
644 | /* now show all non-0 size partitions of it */ | ||
645 | for (n = 0; n < gp->minors - 1; n++) { | ||
646 | struct hd_struct *hd = gp->part[n]; | ||
647 | |||
648 | if (!hd || !hd->nr_sects) | ||
649 | continue; | ||
650 | |||
651 | preempt_disable(); | ||
652 | part_round_stats(hd); | ||
653 | preempt_enable(); | ||
654 | seq_printf(s, "%4d %4d %s %lu %lu %llu " | ||
655 | "%u %lu %lu %llu %u %u %u %u\n", | 975 | "%u %lu %lu %llu %u %u %u %u\n", |
656 | gp->major, n + gp->first_minor + 1, | 976 | MAJOR(part_devt(hd)), MINOR(part_devt(hd)), |
657 | disk_name(gp, n + 1, buf), | 977 | disk_name(gp, hd->partno, buf), |
658 | part_stat_read(hd, ios[0]), | 978 | part_stat_read(hd, ios[0]), |
659 | part_stat_read(hd, merges[0]), | 979 | part_stat_read(hd, merges[0]), |
660 | (unsigned long long)part_stat_read(hd, sectors[0]), | 980 | (unsigned long long)part_stat_read(hd, sectors[0]), |
@@ -668,14 +988,15 @@ static int diskstats_show(struct seq_file *s, void *v) | |||
668 | jiffies_to_msecs(part_stat_read(hd, time_in_queue)) | 988 | jiffies_to_msecs(part_stat_read(hd, time_in_queue)) |
669 | ); | 989 | ); |
670 | } | 990 | } |
991 | disk_part_iter_exit(&piter); | ||
671 | 992 | ||
672 | return 0; | 993 | return 0; |
673 | } | 994 | } |
674 | 995 | ||
675 | const struct seq_operations diskstats_op = { | 996 | const struct seq_operations diskstats_op = { |
676 | .start = diskstats_start, | 997 | .start = disk_seqf_start, |
677 | .next = diskstats_next, | 998 | .next = disk_seqf_next, |
678 | .stop = diskstats_stop, | 999 | .stop = disk_seqf_stop, |
679 | .show = diskstats_show | 1000 | .show = diskstats_show |
680 | }; | 1001 | }; |
681 | #endif /* CONFIG_PROC_FS */ | 1002 | #endif /* CONFIG_PROC_FS */ |
@@ -690,7 +1011,7 @@ static void media_change_notify_thread(struct work_struct *work) | |||
690 | * set enviroment vars to indicate which event this is for | 1011 | * set enviroment vars to indicate which event this is for |
691 | * so that user space will know to go check the media status. | 1012 | * so that user space will know to go check the media status. |
692 | */ | 1013 | */ |
693 | kobject_uevent_env(&gd->dev.kobj, KOBJ_CHANGE, envp); | 1014 | kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp); |
694 | put_device(gd->driverfs_dev); | 1015 | put_device(gd->driverfs_dev); |
695 | } | 1016 | } |
696 | 1017 | ||
@@ -703,42 +1024,29 @@ void genhd_media_change_notify(struct gendisk *disk) | |||
703 | EXPORT_SYMBOL_GPL(genhd_media_change_notify); | 1024 | EXPORT_SYMBOL_GPL(genhd_media_change_notify); |
704 | #endif /* 0 */ | 1025 | #endif /* 0 */ |
705 | 1026 | ||
706 | struct find_block { | 1027 | dev_t blk_lookup_devt(const char *name, int partno) |
707 | const char *name; | ||
708 | int part; | ||
709 | }; | ||
710 | |||
711 | static int match_id(struct device *dev, void *data) | ||
712 | { | 1028 | { |
713 | struct find_block *find = data; | 1029 | dev_t devt = MKDEV(0, 0); |
1030 | struct class_dev_iter iter; | ||
1031 | struct device *dev; | ||
714 | 1032 | ||
715 | if (dev->type != &disk_type) | 1033 | class_dev_iter_init(&iter, &block_class, NULL, &disk_type); |
716 | return 0; | 1034 | while ((dev = class_dev_iter_next(&iter))) { |
717 | if (strcmp(dev->bus_id, find->name) == 0) { | ||
718 | struct gendisk *disk = dev_to_disk(dev); | 1035 | struct gendisk *disk = dev_to_disk(dev); |
719 | if (find->part < disk->minors) | 1036 | struct hd_struct *part; |
720 | return 1; | ||
721 | } | ||
722 | return 0; | ||
723 | } | ||
724 | 1037 | ||
725 | dev_t blk_lookup_devt(const char *name, int part) | 1038 | if (strcmp(dev->bus_id, name)) |
726 | { | 1039 | continue; |
727 | struct device *dev; | ||
728 | dev_t devt = MKDEV(0, 0); | ||
729 | struct find_block find; | ||
730 | 1040 | ||
731 | mutex_lock(&block_class_lock); | 1041 | part = disk_get_part(disk, partno); |
732 | find.name = name; | 1042 | if (part) { |
733 | find.part = part; | 1043 | devt = part_devt(part); |
734 | dev = class_find_device(&block_class, NULL, &find, match_id); | 1044 | disk_put_part(part); |
735 | if (dev) { | 1045 | break; |
736 | put_device(dev); | 1046 | } |
737 | devt = MKDEV(MAJOR(dev->devt), | 1047 | disk_put_part(part); |
738 | MINOR(dev->devt) + part); | ||
739 | } | 1048 | } |
740 | mutex_unlock(&block_class_lock); | 1049 | class_dev_iter_exit(&iter); |
741 | |||
742 | return devt; | 1050 | return devt; |
743 | } | 1051 | } |
744 | EXPORT_SYMBOL(blk_lookup_devt); | 1052 | EXPORT_SYMBOL(blk_lookup_devt); |
@@ -747,6 +1055,7 @@ struct gendisk *alloc_disk(int minors) | |||
747 | { | 1055 | { |
748 | return alloc_disk_node(minors, -1); | 1056 | return alloc_disk_node(minors, -1); |
749 | } | 1057 | } |
1058 | EXPORT_SYMBOL(alloc_disk); | ||
750 | 1059 | ||
751 | struct gendisk *alloc_disk_node(int minors, int node_id) | 1060 | struct gendisk *alloc_disk_node(int minors, int node_id) |
752 | { | 1061 | { |
@@ -755,32 +1064,28 @@ struct gendisk *alloc_disk_node(int minors, int node_id) | |||
755 | disk = kmalloc_node(sizeof(struct gendisk), | 1064 | disk = kmalloc_node(sizeof(struct gendisk), |
756 | GFP_KERNEL | __GFP_ZERO, node_id); | 1065 | GFP_KERNEL | __GFP_ZERO, node_id); |
757 | if (disk) { | 1066 | if (disk) { |
758 | if (!init_disk_stats(disk)) { | 1067 | if (!init_part_stats(&disk->part0)) { |
759 | kfree(disk); | 1068 | kfree(disk); |
760 | return NULL; | 1069 | return NULL; |
761 | } | 1070 | } |
762 | if (minors > 1) { | 1071 | if (disk_expand_part_tbl(disk, 0)) { |
763 | int size = (minors - 1) * sizeof(struct hd_struct *); | 1072 | free_part_stats(&disk->part0); |
764 | disk->part = kmalloc_node(size, | 1073 | kfree(disk); |
765 | GFP_KERNEL | __GFP_ZERO, node_id); | 1074 | return NULL; |
766 | if (!disk->part) { | ||
767 | free_disk_stats(disk); | ||
768 | kfree(disk); | ||
769 | return NULL; | ||
770 | } | ||
771 | } | 1075 | } |
1076 | disk->part_tbl->part[0] = &disk->part0; | ||
1077 | |||
772 | disk->minors = minors; | 1078 | disk->minors = minors; |
773 | rand_initialize_disk(disk); | 1079 | rand_initialize_disk(disk); |
774 | disk->dev.class = &block_class; | 1080 | disk_to_dev(disk)->class = &block_class; |
775 | disk->dev.type = &disk_type; | 1081 | disk_to_dev(disk)->type = &disk_type; |
776 | device_initialize(&disk->dev); | 1082 | device_initialize(disk_to_dev(disk)); |
777 | INIT_WORK(&disk->async_notify, | 1083 | INIT_WORK(&disk->async_notify, |
778 | media_change_notify_thread); | 1084 | media_change_notify_thread); |
1085 | disk->node_id = node_id; | ||
779 | } | 1086 | } |
780 | return disk; | 1087 | return disk; |
781 | } | 1088 | } |
782 | |||
783 | EXPORT_SYMBOL(alloc_disk); | ||
784 | EXPORT_SYMBOL(alloc_disk_node); | 1089 | EXPORT_SYMBOL(alloc_disk_node); |
785 | 1090 | ||
786 | struct kobject *get_disk(struct gendisk *disk) | 1091 | struct kobject *get_disk(struct gendisk *disk) |
@@ -793,7 +1098,7 @@ struct kobject *get_disk(struct gendisk *disk) | |||
793 | owner = disk->fops->owner; | 1098 | owner = disk->fops->owner; |
794 | if (owner && !try_module_get(owner)) | 1099 | if (owner && !try_module_get(owner)) |
795 | return NULL; | 1100 | return NULL; |
796 | kobj = kobject_get(&disk->dev.kobj); | 1101 | kobj = kobject_get(&disk_to_dev(disk)->kobj); |
797 | if (kobj == NULL) { | 1102 | if (kobj == NULL) { |
798 | module_put(owner); | 1103 | module_put(owner); |
799 | return NULL; | 1104 | return NULL; |
@@ -807,27 +1112,28 @@ EXPORT_SYMBOL(get_disk); | |||
807 | void put_disk(struct gendisk *disk) | 1112 | void put_disk(struct gendisk *disk) |
808 | { | 1113 | { |
809 | if (disk) | 1114 | if (disk) |
810 | kobject_put(&disk->dev.kobj); | 1115 | kobject_put(&disk_to_dev(disk)->kobj); |
811 | } | 1116 | } |
812 | 1117 | ||
813 | EXPORT_SYMBOL(put_disk); | 1118 | EXPORT_SYMBOL(put_disk); |
814 | 1119 | ||
815 | void set_device_ro(struct block_device *bdev, int flag) | 1120 | void set_device_ro(struct block_device *bdev, int flag) |
816 | { | 1121 | { |
817 | if (bdev->bd_contains != bdev) | 1122 | bdev->bd_part->policy = flag; |
818 | bdev->bd_part->policy = flag; | ||
819 | else | ||
820 | bdev->bd_disk->policy = flag; | ||
821 | } | 1123 | } |
822 | 1124 | ||
823 | EXPORT_SYMBOL(set_device_ro); | 1125 | EXPORT_SYMBOL(set_device_ro); |
824 | 1126 | ||
825 | void set_disk_ro(struct gendisk *disk, int flag) | 1127 | void set_disk_ro(struct gendisk *disk, int flag) |
826 | { | 1128 | { |
827 | int i; | 1129 | struct disk_part_iter piter; |
828 | disk->policy = flag; | 1130 | struct hd_struct *part; |
829 | for (i = 0; i < disk->minors - 1; i++) | 1131 | |
830 | if (disk->part[i]) disk->part[i]->policy = flag; | 1132 | disk_part_iter_init(&piter, disk, |
1133 | DISK_PITER_INCL_EMPTY | DISK_PITER_INCL_PART0); | ||
1134 | while ((part = disk_part_iter_next(&piter))) | ||
1135 | part->policy = flag; | ||
1136 | disk_part_iter_exit(&piter); | ||
831 | } | 1137 | } |
832 | 1138 | ||
833 | EXPORT_SYMBOL(set_disk_ro); | 1139 | EXPORT_SYMBOL(set_disk_ro); |
@@ -836,18 +1142,15 @@ int bdev_read_only(struct block_device *bdev) | |||
836 | { | 1142 | { |
837 | if (!bdev) | 1143 | if (!bdev) |
838 | return 0; | 1144 | return 0; |
839 | else if (bdev->bd_contains != bdev) | 1145 | return bdev->bd_part->policy; |
840 | return bdev->bd_part->policy; | ||
841 | else | ||
842 | return bdev->bd_disk->policy; | ||
843 | } | 1146 | } |
844 | 1147 | ||
845 | EXPORT_SYMBOL(bdev_read_only); | 1148 | EXPORT_SYMBOL(bdev_read_only); |
846 | 1149 | ||
847 | int invalidate_partition(struct gendisk *disk, int index) | 1150 | int invalidate_partition(struct gendisk *disk, int partno) |
848 | { | 1151 | { |
849 | int res = 0; | 1152 | int res = 0; |
850 | struct block_device *bdev = bdget_disk(disk, index); | 1153 | struct block_device *bdev = bdget_disk(disk, partno); |
851 | if (bdev) { | 1154 | if (bdev) { |
852 | fsync_bdev(bdev); | 1155 | fsync_bdev(bdev); |
853 | res = __invalidate_device(bdev); | 1156 | res = __invalidate_device(bdev); |