xref: /linux/block/genhd.c (revision eddb2e26b5ee3c5da68ba4bf1921ba20e2097bff)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  gendisk handling
31da177e4SLinus Torvalds  */
41da177e4SLinus Torvalds 
51da177e4SLinus Torvalds #include <linux/module.h>
61da177e4SLinus Torvalds #include <linux/fs.h>
71da177e4SLinus Torvalds #include <linux/genhd.h>
8b446b60eSAndrew Morton #include <linux/kdev_t.h>
91da177e4SLinus Torvalds #include <linux/kernel.h>
101da177e4SLinus Torvalds #include <linux/blkdev.h>
111da177e4SLinus Torvalds #include <linux/init.h>
121da177e4SLinus Torvalds #include <linux/spinlock.h>
131da177e4SLinus Torvalds #include <linux/seq_file.h>
141da177e4SLinus Torvalds #include <linux/slab.h>
151da177e4SLinus Torvalds #include <linux/kmod.h>
161da177e4SLinus Torvalds #include <linux/kobj_map.h>
172ef41634SChristoph Hellwig #include <linux/buffer_head.h>
1858383af6SJes Sorensen #include <linux/mutex.h>
19bcce3de1STejun Heo #include <linux/idr.h>
201da177e4SLinus Torvalds 
21ff88972cSAdrian Bunk #include "blk.h"
22ff88972cSAdrian Bunk 
23edfaa7c3SKay Sievers static DEFINE_MUTEX(block_class_lock);
24edfaa7c3SKay Sievers #ifndef CONFIG_SYSFS_DEPRECATED
25edfaa7c3SKay Sievers struct kobject *block_depr;
26edfaa7c3SKay Sievers #endif
271da177e4SLinus Torvalds 
28bcce3de1STejun Heo /* for extended dynamic devt allocation, currently only one major is used */
29bcce3de1STejun Heo #define MAX_EXT_DEVT		(1 << MINORBITS)
30bcce3de1STejun Heo 
31bcce3de1STejun Heo /* For extended devt allocation.  ext_devt_mutex prevents look up
32bcce3de1STejun Heo  * results from going away underneath its user.
33bcce3de1STejun Heo  */
34bcce3de1STejun Heo static DEFINE_MUTEX(ext_devt_mutex);
35bcce3de1STejun Heo static DEFINE_IDR(ext_devt_idr);
36bcce3de1STejun Heo 
371826eadfSAdrian Bunk static struct device_type disk_type;
381826eadfSAdrian Bunk 
39e71bf0d0STejun Heo /**
40e71bf0d0STejun Heo  * disk_get_part - get partition
41e71bf0d0STejun Heo  * @disk: disk to look partition from
42e71bf0d0STejun Heo  * @partno: partition number
43e71bf0d0STejun Heo  *
44e71bf0d0STejun Heo  * Look for partition @partno from @disk.  If found, increment
45e71bf0d0STejun Heo  * reference count and return it.
46e71bf0d0STejun Heo  *
47e71bf0d0STejun Heo  * CONTEXT:
48e71bf0d0STejun Heo  * Don't care.
49e71bf0d0STejun Heo  *
50e71bf0d0STejun Heo  * RETURNS:
51e71bf0d0STejun Heo  * Pointer to the found partition on success, NULL if not found.
52e71bf0d0STejun Heo  */
53e71bf0d0STejun Heo struct hd_struct *disk_get_part(struct gendisk *disk, int partno)
54e71bf0d0STejun Heo {
55e71bf0d0STejun Heo 	struct hd_struct *part;
56e71bf0d0STejun Heo 
57b5d0b9dfSTejun Heo 	if (unlikely(partno < 0 || partno >= disk_max_parts(disk)))
58e71bf0d0STejun Heo 		return NULL;
59e71bf0d0STejun Heo 	rcu_read_lock();
60b5d0b9dfSTejun Heo 	part = rcu_dereference(disk->__part[partno]);
61e71bf0d0STejun Heo 	if (part)
62ed9e1982STejun Heo 		get_device(part_to_dev(part));
63e71bf0d0STejun Heo 	rcu_read_unlock();
64e71bf0d0STejun Heo 
65e71bf0d0STejun Heo 	return part;
66e71bf0d0STejun Heo }
67e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_get_part);
68e71bf0d0STejun Heo 
69e71bf0d0STejun Heo /**
70e71bf0d0STejun Heo  * disk_part_iter_init - initialize partition iterator
71e71bf0d0STejun Heo  * @piter: iterator to initialize
72e71bf0d0STejun Heo  * @disk: disk to iterate over
73e71bf0d0STejun Heo  * @flags: DISK_PITER_* flags
74e71bf0d0STejun Heo  *
75e71bf0d0STejun Heo  * Initialize @piter so that it iterates over partitions of @disk.
76e71bf0d0STejun Heo  *
77e71bf0d0STejun Heo  * CONTEXT:
78e71bf0d0STejun Heo  * Don't care.
79e71bf0d0STejun Heo  */
80e71bf0d0STejun Heo void disk_part_iter_init(struct disk_part_iter *piter, struct gendisk *disk,
81e71bf0d0STejun Heo 			  unsigned int flags)
82e71bf0d0STejun Heo {
83e71bf0d0STejun Heo 	piter->disk = disk;
84e71bf0d0STejun Heo 	piter->part = NULL;
85e71bf0d0STejun Heo 
86e71bf0d0STejun Heo 	if (flags & DISK_PITER_REVERSE)
87e71bf0d0STejun Heo 		piter->idx = disk_max_parts(piter->disk) - 1;
88b5d0b9dfSTejun Heo 	else if (flags & DISK_PITER_INCL_PART0)
89e71bf0d0STejun Heo 		piter->idx = 0;
90b5d0b9dfSTejun Heo 	else
91b5d0b9dfSTejun Heo 		piter->idx = 1;
92e71bf0d0STejun Heo 
93e71bf0d0STejun Heo 	piter->flags = flags;
94e71bf0d0STejun Heo }
95e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_part_iter_init);
96e71bf0d0STejun Heo 
97e71bf0d0STejun Heo /**
98e71bf0d0STejun Heo  * disk_part_iter_next - proceed iterator to the next partition and return it
99e71bf0d0STejun Heo  * @piter: iterator of interest
100e71bf0d0STejun Heo  *
101e71bf0d0STejun Heo  * Proceed @piter to the next partition and return it.
102e71bf0d0STejun Heo  *
103e71bf0d0STejun Heo  * CONTEXT:
104e71bf0d0STejun Heo  * Don't care.
105e71bf0d0STejun Heo  */
106e71bf0d0STejun Heo struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter)
107e71bf0d0STejun Heo {
108e71bf0d0STejun Heo 	int inc, end;
109e71bf0d0STejun Heo 
110e71bf0d0STejun Heo 	/* put the last partition */
111e71bf0d0STejun Heo 	disk_put_part(piter->part);
112e71bf0d0STejun Heo 	piter->part = NULL;
113e71bf0d0STejun Heo 
114e71bf0d0STejun Heo 	rcu_read_lock();
115e71bf0d0STejun Heo 
116e71bf0d0STejun Heo 	/* determine iteration parameters */
117e71bf0d0STejun Heo 	if (piter->flags & DISK_PITER_REVERSE) {
118e71bf0d0STejun Heo 		inc = -1;
119b5d0b9dfSTejun Heo 		if (piter->flags & DISK_PITER_INCL_PART0)
120e71bf0d0STejun Heo 			end = -1;
121b5d0b9dfSTejun Heo 		else
122b5d0b9dfSTejun Heo 			end = 0;
123e71bf0d0STejun Heo 	} else {
124e71bf0d0STejun Heo 		inc = 1;
125e71bf0d0STejun Heo 		end = disk_max_parts(piter->disk);
126e71bf0d0STejun Heo 	}
127e71bf0d0STejun Heo 
128e71bf0d0STejun Heo 	/* iterate to the next partition */
129e71bf0d0STejun Heo 	for (; piter->idx != end; piter->idx += inc) {
130e71bf0d0STejun Heo 		struct hd_struct *part;
131e71bf0d0STejun Heo 
132e71bf0d0STejun Heo 		part = rcu_dereference(piter->disk->__part[piter->idx]);
133e71bf0d0STejun Heo 		if (!part)
134e71bf0d0STejun Heo 			continue;
135e71bf0d0STejun Heo 		if (!(piter->flags & DISK_PITER_INCL_EMPTY) && !part->nr_sects)
136e71bf0d0STejun Heo 			continue;
137e71bf0d0STejun Heo 
138ed9e1982STejun Heo 		get_device(part_to_dev(part));
139e71bf0d0STejun Heo 		piter->part = part;
140e71bf0d0STejun Heo 		piter->idx += inc;
141e71bf0d0STejun Heo 		break;
142e71bf0d0STejun Heo 	}
143e71bf0d0STejun Heo 
144e71bf0d0STejun Heo 	rcu_read_unlock();
145e71bf0d0STejun Heo 
146e71bf0d0STejun Heo 	return piter->part;
147e71bf0d0STejun Heo }
148e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_part_iter_next);
149e71bf0d0STejun Heo 
150e71bf0d0STejun Heo /**
151e71bf0d0STejun Heo  * disk_part_iter_exit - finish up partition iteration
152e71bf0d0STejun Heo  * @piter: iter of interest
153e71bf0d0STejun Heo  *
154e71bf0d0STejun Heo  * Called when iteration is over.  Cleans up @piter.
155e71bf0d0STejun Heo  *
156e71bf0d0STejun Heo  * CONTEXT:
157e71bf0d0STejun Heo  * Don't care.
158e71bf0d0STejun Heo  */
159e71bf0d0STejun Heo void disk_part_iter_exit(struct disk_part_iter *piter)
160e71bf0d0STejun Heo {
161e71bf0d0STejun Heo 	disk_put_part(piter->part);
162e71bf0d0STejun Heo 	piter->part = NULL;
163e71bf0d0STejun Heo }
164e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_part_iter_exit);
165e71bf0d0STejun Heo 
166e71bf0d0STejun Heo /**
167e71bf0d0STejun Heo  * disk_map_sector_rcu - map sector to partition
168e71bf0d0STejun Heo  * @disk: gendisk of interest
169e71bf0d0STejun Heo  * @sector: sector to map
170e71bf0d0STejun Heo  *
171e71bf0d0STejun Heo  * Find out which partition @sector maps to on @disk.  This is
172e71bf0d0STejun Heo  * primarily used for stats accounting.
173e71bf0d0STejun Heo  *
174e71bf0d0STejun Heo  * CONTEXT:
175e71bf0d0STejun Heo  * RCU read locked.  The returned partition pointer is valid only
176e71bf0d0STejun Heo  * while preemption is disabled.
177e71bf0d0STejun Heo  *
178e71bf0d0STejun Heo  * RETURNS:
179e71bf0d0STejun Heo  * Found partition on success, NULL if there's no matching partition.
180e71bf0d0STejun Heo  */
181e71bf0d0STejun Heo struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector)
182e71bf0d0STejun Heo {
183e71bf0d0STejun Heo 	int i;
184e71bf0d0STejun Heo 
185b5d0b9dfSTejun Heo 	for (i = 1; i < disk_max_parts(disk); i++) {
186e71bf0d0STejun Heo 		struct hd_struct *part = rcu_dereference(disk->__part[i]);
187e71bf0d0STejun Heo 
188e71bf0d0STejun Heo 		if (part && part->start_sect <= sector &&
189e71bf0d0STejun Heo 		    sector < part->start_sect + part->nr_sects)
190e71bf0d0STejun Heo 			return part;
191e71bf0d0STejun Heo 	}
192e71bf0d0STejun Heo 	return NULL;
193e71bf0d0STejun Heo }
194e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_map_sector_rcu);
195e71bf0d0STejun Heo 
1961da177e4SLinus Torvalds /*
1971da177e4SLinus Torvalds  * Can be deleted altogether. Later.
1981da177e4SLinus Torvalds  *
1991da177e4SLinus Torvalds  */
2001da177e4SLinus Torvalds static struct blk_major_name {
2011da177e4SLinus Torvalds 	struct blk_major_name *next;
2021da177e4SLinus Torvalds 	int major;
2031da177e4SLinus Torvalds 	char name[16];
20468eef3b4SJoe Korty } *major_names[BLKDEV_MAJOR_HASH_SIZE];
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds /* index in the above - for now: assume no multimajor ranges */
2071da177e4SLinus Torvalds static inline int major_to_index(int major)
2081da177e4SLinus Torvalds {
20968eef3b4SJoe Korty 	return major % BLKDEV_MAJOR_HASH_SIZE;
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds 
21268eef3b4SJoe Korty #ifdef CONFIG_PROC_FS
213cf771cb5STejun Heo void blkdev_show(struct seq_file *seqf, off_t offset)
2147170be5fSNeil Horman {
21568eef3b4SJoe Korty 	struct blk_major_name *dp;
2167170be5fSNeil Horman 
21768eef3b4SJoe Korty 	if (offset < BLKDEV_MAJOR_HASH_SIZE) {
218edfaa7c3SKay Sievers 		mutex_lock(&block_class_lock);
21968eef3b4SJoe Korty 		for (dp = major_names[offset]; dp; dp = dp->next)
220cf771cb5STejun Heo 			seq_printf(seqf, "%3d %s\n", dp->major, dp->name);
221edfaa7c3SKay Sievers 		mutex_unlock(&block_class_lock);
22268eef3b4SJoe Korty 	}
2237170be5fSNeil Horman }
22468eef3b4SJoe Korty #endif /* CONFIG_PROC_FS */
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds int register_blkdev(unsigned int major, const char *name)
2271da177e4SLinus Torvalds {
2281da177e4SLinus Torvalds 	struct blk_major_name **n, *p;
2291da177e4SLinus Torvalds 	int index, ret = 0;
2301da177e4SLinus Torvalds 
231edfaa7c3SKay Sievers 	mutex_lock(&block_class_lock);
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	/* temporary */
2341da177e4SLinus Torvalds 	if (major == 0) {
2351da177e4SLinus Torvalds 		for (index = ARRAY_SIZE(major_names)-1; index > 0; index--) {
2361da177e4SLinus Torvalds 			if (major_names[index] == NULL)
2371da177e4SLinus Torvalds 				break;
2381da177e4SLinus Torvalds 		}
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 		if (index == 0) {
2411da177e4SLinus Torvalds 			printk("register_blkdev: failed to get major for %s\n",
2421da177e4SLinus Torvalds 			       name);
2431da177e4SLinus Torvalds 			ret = -EBUSY;
2441da177e4SLinus Torvalds 			goto out;
2451da177e4SLinus Torvalds 		}
2461da177e4SLinus Torvalds 		major = index;
2471da177e4SLinus Torvalds 		ret = major;
2481da177e4SLinus Torvalds 	}
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 	p = kmalloc(sizeof(struct blk_major_name), GFP_KERNEL);
2511da177e4SLinus Torvalds 	if (p == NULL) {
2521da177e4SLinus Torvalds 		ret = -ENOMEM;
2531da177e4SLinus Torvalds 		goto out;
2541da177e4SLinus Torvalds 	}
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds 	p->major = major;
2571da177e4SLinus Torvalds 	strlcpy(p->name, name, sizeof(p->name));
2581da177e4SLinus Torvalds 	p->next = NULL;
2591da177e4SLinus Torvalds 	index = major_to_index(major);
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds 	for (n = &major_names[index]; *n; n = &(*n)->next) {
2621da177e4SLinus Torvalds 		if ((*n)->major == major)
2631da177e4SLinus Torvalds 			break;
2641da177e4SLinus Torvalds 	}
2651da177e4SLinus Torvalds 	if (!*n)
2661da177e4SLinus Torvalds 		*n = p;
2671da177e4SLinus Torvalds 	else
2681da177e4SLinus Torvalds 		ret = -EBUSY;
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds 	if (ret < 0) {
2711da177e4SLinus Torvalds 		printk("register_blkdev: cannot get major %d for %s\n",
2721da177e4SLinus Torvalds 		       major, name);
2731da177e4SLinus Torvalds 		kfree(p);
2741da177e4SLinus Torvalds 	}
2751da177e4SLinus Torvalds out:
276edfaa7c3SKay Sievers 	mutex_unlock(&block_class_lock);
2771da177e4SLinus Torvalds 	return ret;
2781da177e4SLinus Torvalds }
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds EXPORT_SYMBOL(register_blkdev);
2811da177e4SLinus Torvalds 
282f4480240SAkinobu Mita void unregister_blkdev(unsigned int major, const char *name)
2831da177e4SLinus Torvalds {
2841da177e4SLinus Torvalds 	struct blk_major_name **n;
2851da177e4SLinus Torvalds 	struct blk_major_name *p = NULL;
2861da177e4SLinus Torvalds 	int index = major_to_index(major);
2871da177e4SLinus Torvalds 
288edfaa7c3SKay Sievers 	mutex_lock(&block_class_lock);
2891da177e4SLinus Torvalds 	for (n = &major_names[index]; *n; n = &(*n)->next)
2901da177e4SLinus Torvalds 		if ((*n)->major == major)
2911da177e4SLinus Torvalds 			break;
292294462a5SAkinobu Mita 	if (!*n || strcmp((*n)->name, name)) {
293294462a5SAkinobu Mita 		WARN_ON(1);
294294462a5SAkinobu Mita 	} else {
2951da177e4SLinus Torvalds 		p = *n;
2961da177e4SLinus Torvalds 		*n = p->next;
2971da177e4SLinus Torvalds 	}
298edfaa7c3SKay Sievers 	mutex_unlock(&block_class_lock);
2991da177e4SLinus Torvalds 	kfree(p);
3001da177e4SLinus Torvalds }
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds EXPORT_SYMBOL(unregister_blkdev);
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds static struct kobj_map *bdev_map;
3051da177e4SLinus Torvalds 
306bcce3de1STejun Heo /**
307870d6656STejun Heo  * blk_mangle_minor - scatter minor numbers apart
308870d6656STejun Heo  * @minor: minor number to mangle
309870d6656STejun Heo  *
310870d6656STejun Heo  * Scatter consecutively allocated @minor number apart if MANGLE_DEVT
311870d6656STejun Heo  * is enabled.  Mangling twice gives the original value.
312870d6656STejun Heo  *
313870d6656STejun Heo  * RETURNS:
314870d6656STejun Heo  * Mangled value.
315870d6656STejun Heo  *
316870d6656STejun Heo  * CONTEXT:
317870d6656STejun Heo  * Don't care.
318870d6656STejun Heo  */
319870d6656STejun Heo static int blk_mangle_minor(int minor)
320870d6656STejun Heo {
321870d6656STejun Heo #ifdef CONFIG_DEBUG_BLOCK_EXT_DEVT
322870d6656STejun Heo 	int i;
323870d6656STejun Heo 
324870d6656STejun Heo 	for (i = 0; i < MINORBITS / 2; i++) {
325870d6656STejun Heo 		int low = minor & (1 << i);
326870d6656STejun Heo 		int high = minor & (1 << (MINORBITS - 1 - i));
327870d6656STejun Heo 		int distance = MINORBITS - 1 - 2 * i;
328870d6656STejun Heo 
329870d6656STejun Heo 		minor ^= low | high;	/* clear both bits */
330870d6656STejun Heo 		low <<= distance;	/* swap the positions */
331870d6656STejun Heo 		high >>= distance;
332870d6656STejun Heo 		minor |= low | high;	/* and set */
333870d6656STejun Heo 	}
334870d6656STejun Heo #endif
335870d6656STejun Heo 	return minor;
336870d6656STejun Heo }
337870d6656STejun Heo 
338870d6656STejun Heo /**
339bcce3de1STejun Heo  * blk_alloc_devt - allocate a dev_t for a partition
340bcce3de1STejun Heo  * @part: partition to allocate dev_t for
341bcce3de1STejun Heo  * @gfp_mask: memory allocation flag
342bcce3de1STejun Heo  * @devt: out parameter for resulting dev_t
343bcce3de1STejun Heo  *
344bcce3de1STejun Heo  * Allocate a dev_t for block device.
345bcce3de1STejun Heo  *
346bcce3de1STejun Heo  * RETURNS:
347bcce3de1STejun Heo  * 0 on success, allocated dev_t is returned in *@devt.  -errno on
348bcce3de1STejun Heo  * failure.
349bcce3de1STejun Heo  *
350bcce3de1STejun Heo  * CONTEXT:
351bcce3de1STejun Heo  * Might sleep.
352bcce3de1STejun Heo  */
353bcce3de1STejun Heo int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
354bcce3de1STejun Heo {
355bcce3de1STejun Heo 	struct gendisk *disk = part_to_disk(part);
356bcce3de1STejun Heo 	int idx, rc;
357bcce3de1STejun Heo 
358bcce3de1STejun Heo 	/* in consecutive minor range? */
359bcce3de1STejun Heo 	if (part->partno < disk->minors) {
360bcce3de1STejun Heo 		*devt = MKDEV(disk->major, disk->first_minor + part->partno);
361bcce3de1STejun Heo 		return 0;
362bcce3de1STejun Heo 	}
363bcce3de1STejun Heo 
364bcce3de1STejun Heo 	/* allocate ext devt */
365bcce3de1STejun Heo 	do {
366bcce3de1STejun Heo 		if (!idr_pre_get(&ext_devt_idr, GFP_KERNEL))
367bcce3de1STejun Heo 			return -ENOMEM;
368bcce3de1STejun Heo 		rc = idr_get_new(&ext_devt_idr, part, &idx);
369bcce3de1STejun Heo 	} while (rc == -EAGAIN);
370bcce3de1STejun Heo 
371bcce3de1STejun Heo 	if (rc)
372bcce3de1STejun Heo 		return rc;
373bcce3de1STejun Heo 
374bcce3de1STejun Heo 	if (idx > MAX_EXT_DEVT) {
375bcce3de1STejun Heo 		idr_remove(&ext_devt_idr, idx);
376bcce3de1STejun Heo 		return -EBUSY;
377bcce3de1STejun Heo 	}
378bcce3de1STejun Heo 
379870d6656STejun Heo 	*devt = MKDEV(BLOCK_EXT_MAJOR, blk_mangle_minor(idx));
380bcce3de1STejun Heo 	return 0;
381bcce3de1STejun Heo }
382bcce3de1STejun Heo 
383bcce3de1STejun Heo /**
384bcce3de1STejun Heo  * blk_free_devt - free a dev_t
385bcce3de1STejun Heo  * @devt: dev_t to free
386bcce3de1STejun Heo  *
387bcce3de1STejun Heo  * Free @devt which was allocated using blk_alloc_devt().
388bcce3de1STejun Heo  *
389bcce3de1STejun Heo  * CONTEXT:
390bcce3de1STejun Heo  * Might sleep.
391bcce3de1STejun Heo  */
392bcce3de1STejun Heo void blk_free_devt(dev_t devt)
393bcce3de1STejun Heo {
394bcce3de1STejun Heo 	might_sleep();
395bcce3de1STejun Heo 
396bcce3de1STejun Heo 	if (devt == MKDEV(0, 0))
397bcce3de1STejun Heo 		return;
398bcce3de1STejun Heo 
399bcce3de1STejun Heo 	if (MAJOR(devt) == BLOCK_EXT_MAJOR) {
400bcce3de1STejun Heo 		mutex_lock(&ext_devt_mutex);
401870d6656STejun Heo 		idr_remove(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
402bcce3de1STejun Heo 		mutex_unlock(&ext_devt_mutex);
403bcce3de1STejun Heo 	}
404bcce3de1STejun Heo }
405bcce3de1STejun Heo 
4061f014290STejun Heo static char *bdevt_str(dev_t devt, char *buf)
4071f014290STejun Heo {
4081f014290STejun Heo 	if (MAJOR(devt) <= 0xff && MINOR(devt) <= 0xff) {
4091f014290STejun Heo 		char tbuf[BDEVT_SIZE];
4101f014290STejun Heo 		snprintf(tbuf, BDEVT_SIZE, "%02x%02x", MAJOR(devt), MINOR(devt));
4111f014290STejun Heo 		snprintf(buf, BDEVT_SIZE, "%-9s", tbuf);
4121f014290STejun Heo 	} else
4131f014290STejun Heo 		snprintf(buf, BDEVT_SIZE, "%03x:%05x", MAJOR(devt), MINOR(devt));
4141f014290STejun Heo 
4151f014290STejun Heo 	return buf;
4161f014290STejun Heo }
4171f014290STejun Heo 
4181da177e4SLinus Torvalds /*
4191da177e4SLinus Torvalds  * Register device numbers dev..(dev+range-1)
4201da177e4SLinus Torvalds  * range must be nonzero
4211da177e4SLinus Torvalds  * The hash chain is sorted on range, so that subranges can override.
4221da177e4SLinus Torvalds  */
423edfaa7c3SKay Sievers void blk_register_region(dev_t devt, unsigned long range, struct module *module,
4241da177e4SLinus Torvalds 			 struct kobject *(*probe)(dev_t, int *, void *),
4251da177e4SLinus Torvalds 			 int (*lock)(dev_t, void *), void *data)
4261da177e4SLinus Torvalds {
427edfaa7c3SKay Sievers 	kobj_map(bdev_map, devt, range, module, probe, lock, data);
4281da177e4SLinus Torvalds }
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds EXPORT_SYMBOL(blk_register_region);
4311da177e4SLinus Torvalds 
432edfaa7c3SKay Sievers void blk_unregister_region(dev_t devt, unsigned long range)
4331da177e4SLinus Torvalds {
434edfaa7c3SKay Sievers 	kobj_unmap(bdev_map, devt, range);
4351da177e4SLinus Torvalds }
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds EXPORT_SYMBOL(blk_unregister_region);
4381da177e4SLinus Torvalds 
439cf771cb5STejun Heo static struct kobject *exact_match(dev_t devt, int *partno, void *data)
4401da177e4SLinus Torvalds {
4411da177e4SLinus Torvalds 	struct gendisk *p = data;
442edfaa7c3SKay Sievers 
443ed9e1982STejun Heo 	return &disk_to_dev(p)->kobj;
4441da177e4SLinus Torvalds }
4451da177e4SLinus Torvalds 
446edfaa7c3SKay Sievers static int exact_lock(dev_t devt, void *data)
4471da177e4SLinus Torvalds {
4481da177e4SLinus Torvalds 	struct gendisk *p = data;
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds 	if (!get_disk(p))
4511da177e4SLinus Torvalds 		return -1;
4521da177e4SLinus Torvalds 	return 0;
4531da177e4SLinus Torvalds }
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds /**
4561da177e4SLinus Torvalds  * add_disk - add partitioning information to kernel list
4571da177e4SLinus Torvalds  * @disk: per-device partitioning information
4581da177e4SLinus Torvalds  *
4591da177e4SLinus Torvalds  * This function registers the partitioning information in @disk
4601da177e4SLinus Torvalds  * with the kernel.
4611da177e4SLinus Torvalds  */
4621da177e4SLinus Torvalds void add_disk(struct gendisk *disk)
4631da177e4SLinus Torvalds {
464cf0ca9feSPeter Zijlstra 	struct backing_dev_info *bdi;
4656ffeea77SGreg Kroah-Hartman 	int retval;
466cf0ca9feSPeter Zijlstra 
4671da177e4SLinus Torvalds 	disk->flags |= GENHD_FL_UP;
468ed9e1982STejun Heo 	disk_to_dev(disk)->devt = MKDEV(disk->major, disk->first_minor);
469f331c029STejun Heo 	blk_register_region(disk_devt(disk), disk->minors, NULL,
470f331c029STejun Heo 			    exact_match, exact_lock, disk);
4711da177e4SLinus Torvalds 	register_disk(disk);
4721da177e4SLinus Torvalds 	blk_register_queue(disk);
473cf0ca9feSPeter Zijlstra 
474cf0ca9feSPeter Zijlstra 	bdi = &disk->queue->backing_dev_info;
475f331c029STejun Heo 	bdi_register_dev(bdi, disk_devt(disk));
476ed9e1982STejun Heo 	retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj,
477ed9e1982STejun Heo 				   "bdi");
4786ffeea77SGreg Kroah-Hartman 	WARN_ON(retval);
4791da177e4SLinus Torvalds }
4801da177e4SLinus Torvalds 
4811da177e4SLinus Torvalds EXPORT_SYMBOL(add_disk);
4821da177e4SLinus Torvalds EXPORT_SYMBOL(del_gendisk);	/* in partitions/check.c */
4831da177e4SLinus Torvalds 
4841da177e4SLinus Torvalds void unlink_gendisk(struct gendisk *disk)
4851da177e4SLinus Torvalds {
486ed9e1982STejun Heo 	sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi");
487cf0ca9feSPeter Zijlstra 	bdi_unregister(&disk->queue->backing_dev_info);
4881da177e4SLinus Torvalds 	blk_unregister_queue(disk);
489f331c029STejun Heo 	blk_unregister_region(disk_devt(disk), disk->minors);
4901da177e4SLinus Torvalds }
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds /**
4931da177e4SLinus Torvalds  * get_gendisk - get partitioning information for a given device
494710027a4SRandy Dunlap  * @devt: device to get partitioning information for
495710027a4SRandy Dunlap  * @part: returned partition index
4961da177e4SLinus Torvalds  *
4971da177e4SLinus Torvalds  * This function gets the structure containing partitioning
498710027a4SRandy Dunlap  * information for the given device @devt.
4991da177e4SLinus Torvalds  */
500cf771cb5STejun Heo struct gendisk *get_gendisk(dev_t devt, int *partno)
5011da177e4SLinus Torvalds {
502bcce3de1STejun Heo 	struct gendisk *disk = NULL;
503edfaa7c3SKay Sievers 
504bcce3de1STejun Heo 	if (MAJOR(devt) != BLOCK_EXT_MAJOR) {
505bcce3de1STejun Heo 		struct kobject *kobj;
506bcce3de1STejun Heo 
507bcce3de1STejun Heo 		kobj = kobj_lookup(bdev_map, devt, partno);
508bcce3de1STejun Heo 		if (kobj)
509bcce3de1STejun Heo 			disk = dev_to_disk(kobj_to_dev(kobj));
510bcce3de1STejun Heo 	} else {
511bcce3de1STejun Heo 		struct hd_struct *part;
512bcce3de1STejun Heo 
513bcce3de1STejun Heo 		mutex_lock(&ext_devt_mutex);
514870d6656STejun Heo 		part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
515bcce3de1STejun Heo 		if (part && get_disk(part_to_disk(part))) {
516bcce3de1STejun Heo 			*partno = part->partno;
517bcce3de1STejun Heo 			disk = part_to_disk(part);
518bcce3de1STejun Heo 		}
519bcce3de1STejun Heo 		mutex_unlock(&ext_devt_mutex);
520bcce3de1STejun Heo 	}
521bcce3de1STejun Heo 
522bcce3de1STejun Heo 	return disk;
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds 
525f331c029STejun Heo /**
526f331c029STejun Heo  * bdget_disk - do bdget() by gendisk and partition number
527f331c029STejun Heo  * @disk: gendisk of interest
528f331c029STejun Heo  * @partno: partition number
529f331c029STejun Heo  *
530f331c029STejun Heo  * Find partition @partno from @disk, do bdget() on it.
531f331c029STejun Heo  *
532f331c029STejun Heo  * CONTEXT:
533f331c029STejun Heo  * Don't care.
534f331c029STejun Heo  *
535f331c029STejun Heo  * RETURNS:
536f331c029STejun Heo  * Resulting block_device on success, NULL on failure.
537f331c029STejun Heo  */
538f331c029STejun Heo extern struct block_device *bdget_disk(struct gendisk *disk, int partno)
539f331c029STejun Heo {
540e71bf0d0STejun Heo 	struct hd_struct *part;
541548b10ebSTejun Heo 	struct block_device *bdev = NULL;
542f331c029STejun Heo 
543e71bf0d0STejun Heo 	part = disk_get_part(disk, partno);
544548b10ebSTejun Heo 	if (part && (part->nr_sects || partno == 0))
545548b10ebSTejun Heo 		bdev = bdget(part_devt(part));
546e71bf0d0STejun Heo 	disk_put_part(part);
547f331c029STejun Heo 
548548b10ebSTejun Heo 	return bdev;
549f331c029STejun Heo }
550f331c029STejun Heo EXPORT_SYMBOL(bdget_disk);
551f331c029STejun Heo 
552dd2a345fSDave Gilbert /*
5535c6f35c5SGreg Kroah-Hartman  * print a full list of all partitions - intended for places where the root
5545c6f35c5SGreg Kroah-Hartman  * filesystem can't be mounted and thus to give the victim some idea of what
5555c6f35c5SGreg Kroah-Hartman  * went wrong
5565c6f35c5SGreg Kroah-Hartman  */
5575c6f35c5SGreg Kroah-Hartman void __init printk_all_partitions(void)
5585c6f35c5SGreg Kroah-Hartman {
559def4e38dSTejun Heo 	struct class_dev_iter iter;
560def4e38dSTejun Heo 	struct device *dev;
561def4e38dSTejun Heo 
562def4e38dSTejun Heo 	class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
563def4e38dSTejun Heo 	while ((dev = class_dev_iter_next(&iter))) {
564def4e38dSTejun Heo 		struct gendisk *disk = dev_to_disk(dev);
565e71bf0d0STejun Heo 		struct disk_part_iter piter;
566e71bf0d0STejun Heo 		struct hd_struct *part;
5671f014290STejun Heo 		char name_buf[BDEVNAME_SIZE];
5681f014290STejun Heo 		char devt_buf[BDEVT_SIZE];
569def4e38dSTejun Heo 
570def4e38dSTejun Heo 		/*
571def4e38dSTejun Heo 		 * Don't show empty devices or things that have been
572def4e38dSTejun Heo 		 * surpressed
573def4e38dSTejun Heo 		 */
574def4e38dSTejun Heo 		if (get_capacity(disk) == 0 ||
575def4e38dSTejun Heo 		    (disk->flags & GENHD_FL_SUPPRESS_PARTITION_INFO))
576def4e38dSTejun Heo 			continue;
577def4e38dSTejun Heo 
578def4e38dSTejun Heo 		/*
579def4e38dSTejun Heo 		 * Note, unlike /proc/partitions, I am showing the
580def4e38dSTejun Heo 		 * numbers in hex - the same format as the root=
581def4e38dSTejun Heo 		 * option takes.
582def4e38dSTejun Heo 		 */
5831f014290STejun Heo 		printk("%s %10llu %s",
5841f014290STejun Heo 		       bdevt_str(disk_devt(disk), devt_buf),
585def4e38dSTejun Heo 		       (unsigned long long)get_capacity(disk) >> 1,
5861f014290STejun Heo 		       disk_name(disk, 0, name_buf));
587def4e38dSTejun Heo 		if (disk->driverfs_dev != NULL &&
588def4e38dSTejun Heo 		    disk->driverfs_dev->driver != NULL)
589def4e38dSTejun Heo 			printk(" driver: %s\n",
590def4e38dSTejun Heo 			       disk->driverfs_dev->driver->name);
591def4e38dSTejun Heo 		else
592def4e38dSTejun Heo 			printk(" (driver?)\n");
593def4e38dSTejun Heo 
594def4e38dSTejun Heo 		/* now show the partitions */
595e71bf0d0STejun Heo 		disk_part_iter_init(&piter, disk, 0);
596e71bf0d0STejun Heo 		while ((part = disk_part_iter_next(&piter)))
5971f014290STejun Heo 			printk("  %s %10llu %s\n",
5981f014290STejun Heo 			       bdevt_str(part_devt(part), devt_buf),
599f331c029STejun Heo 			       (unsigned long long)part->nr_sects >> 1,
6001f014290STejun Heo 			       disk_name(disk, part->partno, name_buf));
601e71bf0d0STejun Heo 		disk_part_iter_exit(&piter);
602def4e38dSTejun Heo 	}
603def4e38dSTejun Heo 	class_dev_iter_exit(&iter);
604dd2a345fSDave Gilbert }
605dd2a345fSDave Gilbert 
6061da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
6071da177e4SLinus Torvalds /* iterator */
608def4e38dSTejun Heo static void *disk_seqf_start(struct seq_file *seqf, loff_t *pos)
60968c4d4a7SGreg Kroah-Hartman {
610def4e38dSTejun Heo 	loff_t skip = *pos;
611def4e38dSTejun Heo 	struct class_dev_iter *iter;
612def4e38dSTejun Heo 	struct device *dev;
61368c4d4a7SGreg Kroah-Hartman 
614def4e38dSTejun Heo 	iter = kmalloc(GFP_KERNEL, sizeof(*iter));
615def4e38dSTejun Heo 	if (!iter)
616def4e38dSTejun Heo 		return ERR_PTR(-ENOMEM);
617def4e38dSTejun Heo 
618def4e38dSTejun Heo 	seqf->private = iter;
619def4e38dSTejun Heo 	class_dev_iter_init(iter, &block_class, NULL, &disk_type);
620def4e38dSTejun Heo 	do {
621def4e38dSTejun Heo 		dev = class_dev_iter_next(iter);
622def4e38dSTejun Heo 		if (!dev)
623def4e38dSTejun Heo 			return NULL;
624def4e38dSTejun Heo 	} while (skip--);
625def4e38dSTejun Heo 
626def4e38dSTejun Heo 	return dev_to_disk(dev);
62768c4d4a7SGreg Kroah-Hartman }
62868c4d4a7SGreg Kroah-Hartman 
629def4e38dSTejun Heo static void *disk_seqf_next(struct seq_file *seqf, void *v, loff_t *pos)
6301da177e4SLinus Torvalds {
631edfaa7c3SKay Sievers 	struct device *dev;
63266c64afeSGreg Kroah-Hartman 
633def4e38dSTejun Heo 	(*pos)++;
634def4e38dSTejun Heo 	dev = class_dev_iter_next(seqf->private);
6352ac3cee5STejun Heo 	if (dev)
636edfaa7c3SKay Sievers 		return dev_to_disk(dev);
6372ac3cee5STejun Heo 
6381da177e4SLinus Torvalds 	return NULL;
6391da177e4SLinus Torvalds }
6401da177e4SLinus Torvalds 
641def4e38dSTejun Heo static void disk_seqf_stop(struct seq_file *seqf, void *v)
64227f30251SGreg Kroah-Hartman {
643def4e38dSTejun Heo 	struct class_dev_iter *iter = seqf->private;
644def4e38dSTejun Heo 
645def4e38dSTejun Heo 	/* stop is called even after start failed :-( */
646def4e38dSTejun Heo 	if (iter) {
647def4e38dSTejun Heo 		class_dev_iter_exit(iter);
648def4e38dSTejun Heo 		kfree(iter);
649def4e38dSTejun Heo 	}
65027f30251SGreg Kroah-Hartman }
65127f30251SGreg Kroah-Hartman 
652def4e38dSTejun Heo static void *show_partition_start(struct seq_file *seqf, loff_t *pos)
6531da177e4SLinus Torvalds {
654def4e38dSTejun Heo 	static void *p;
6551da177e4SLinus Torvalds 
656def4e38dSTejun Heo 	p = disk_seqf_start(seqf, pos);
657def4e38dSTejun Heo 	if (!IS_ERR(p) && p)
658def4e38dSTejun Heo 		seq_puts(seqf, "major minor  #blocks  name\n\n");
659def4e38dSTejun Heo 	return p;
6601da177e4SLinus Torvalds }
6611da177e4SLinus Torvalds 
662cf771cb5STejun Heo static int show_partition(struct seq_file *seqf, void *v)
6631da177e4SLinus Torvalds {
6641da177e4SLinus Torvalds 	struct gendisk *sgp = v;
665e71bf0d0STejun Heo 	struct disk_part_iter piter;
666e71bf0d0STejun Heo 	struct hd_struct *part;
6671da177e4SLinus Torvalds 	char buf[BDEVNAME_SIZE];
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 	/* Don't show non-partitionable removeable devices or empty devices */
670b5d0b9dfSTejun Heo 	if (!get_capacity(sgp) || (!disk_partitionable(sgp) &&
671f331c029STejun Heo 				   (sgp->flags & GENHD_FL_REMOVABLE)))
6721da177e4SLinus Torvalds 		return 0;
6731da177e4SLinus Torvalds 	if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO)
6741da177e4SLinus Torvalds 		return 0;
6751da177e4SLinus Torvalds 
6761da177e4SLinus Torvalds 	/* show the full disk and all non-0 size partitions of it */
6771f014290STejun Heo 	seq_printf(seqf, "%4d  %7d %10llu %s\n",
678f331c029STejun Heo 		MAJOR(disk_devt(sgp)), MINOR(disk_devt(sgp)),
6791da177e4SLinus Torvalds 		(unsigned long long)get_capacity(sgp) >> 1,
6801da177e4SLinus Torvalds 		disk_name(sgp, 0, buf));
681e71bf0d0STejun Heo 
682e71bf0d0STejun Heo 	disk_part_iter_init(&piter, sgp, 0);
683e71bf0d0STejun Heo 	while ((part = disk_part_iter_next(&piter)))
6841f014290STejun Heo 		seq_printf(seqf, "%4d  %7d %10llu %s\n",
685f331c029STejun Heo 			   MAJOR(part_devt(part)), MINOR(part_devt(part)),
686f331c029STejun Heo 			   (unsigned long long)part->nr_sects >> 1,
687f331c029STejun Heo 			   disk_name(sgp, part->partno, buf));
688e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
6891da177e4SLinus Torvalds 
6901da177e4SLinus Torvalds 	return 0;
6911da177e4SLinus Torvalds }
6921da177e4SLinus Torvalds 
69312f32bb3SJan Engelhardt const struct seq_operations partitions_op = {
694def4e38dSTejun Heo 	.start	= show_partition_start,
695def4e38dSTejun Heo 	.next	= disk_seqf_next,
696def4e38dSTejun Heo 	.stop	= disk_seqf_stop,
6971da177e4SLinus Torvalds 	.show	= show_partition
6981da177e4SLinus Torvalds };
6991da177e4SLinus Torvalds #endif
7001da177e4SLinus Torvalds 
7011da177e4SLinus Torvalds 
702cf771cb5STejun Heo static struct kobject *base_probe(dev_t devt, int *partno, void *data)
7031da177e4SLinus Torvalds {
704edfaa7c3SKay Sievers 	if (request_module("block-major-%d-%d", MAJOR(devt), MINOR(devt)) > 0)
7051da177e4SLinus Torvalds 		/* Make old-style 2.4 aliases work */
706edfaa7c3SKay Sievers 		request_module("block-major-%d", MAJOR(devt));
7071da177e4SLinus Torvalds 	return NULL;
7081da177e4SLinus Torvalds }
7091da177e4SLinus Torvalds 
7101da177e4SLinus Torvalds static int __init genhd_device_init(void)
7111da177e4SLinus Torvalds {
712e105b8bfSDan Williams 	int error;
713e105b8bfSDan Williams 
714e105b8bfSDan Williams 	block_class.dev_kobj = sysfs_dev_block_kobj;
715e105b8bfSDan Williams 	error = class_register(&block_class);
716ee27a558SRoland McGrath 	if (unlikely(error))
717ee27a558SRoland McGrath 		return error;
718edfaa7c3SKay Sievers 	bdev_map = kobj_map_init(base_probe, &block_class_lock);
7191da177e4SLinus Torvalds 	blk_dev_init();
720edfaa7c3SKay Sievers 
721edfaa7c3SKay Sievers #ifndef CONFIG_SYSFS_DEPRECATED
722edfaa7c3SKay Sievers 	/* create top-level block dir */
723edfaa7c3SKay Sievers 	block_depr = kobject_create_and_add("block", NULL);
724edfaa7c3SKay Sievers #endif
725830d3cfbSGreg Kroah-Hartman 	return 0;
7261da177e4SLinus Torvalds }
7271da177e4SLinus Torvalds 
7281da177e4SLinus Torvalds subsys_initcall(genhd_device_init);
7291da177e4SLinus Torvalds 
730edfaa7c3SKay Sievers static ssize_t disk_range_show(struct device *dev,
731edfaa7c3SKay Sievers 			       struct device_attribute *attr, char *buf)
7321da177e4SLinus Torvalds {
733edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
7341da177e4SLinus Torvalds 
735edfaa7c3SKay Sievers 	return sprintf(buf, "%d\n", disk->minors);
7361da177e4SLinus Torvalds }
7371da177e4SLinus Torvalds 
7381f014290STejun Heo static ssize_t disk_ext_range_show(struct device *dev,
7391f014290STejun Heo 				   struct device_attribute *attr, char *buf)
7401f014290STejun Heo {
7411f014290STejun Heo 	struct gendisk *disk = dev_to_disk(dev);
7421f014290STejun Heo 
743b5d0b9dfSTejun Heo 	return sprintf(buf, "%d\n", disk_max_parts(disk));
7441f014290STejun Heo }
7451f014290STejun Heo 
746edfaa7c3SKay Sievers static ssize_t disk_removable_show(struct device *dev,
747edfaa7c3SKay Sievers 				   struct device_attribute *attr, char *buf)
748a7fd6706SKay Sievers {
749edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
750a7fd6706SKay Sievers 
751edfaa7c3SKay Sievers 	return sprintf(buf, "%d\n",
7521da177e4SLinus Torvalds 		       (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0));
753edfaa7c3SKay Sievers }
7541da177e4SLinus Torvalds 
7551c9ce527SKay Sievers static ssize_t disk_ro_show(struct device *dev,
7561c9ce527SKay Sievers 				   struct device_attribute *attr, char *buf)
7571c9ce527SKay Sievers {
7581c9ce527SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
7591c9ce527SKay Sievers 
760b7db9956STejun Heo 	return sprintf(buf, "%d\n", get_disk_ro(disk) ? 1 : 0);
7611c9ce527SKay Sievers }
7621c9ce527SKay Sievers 
763edfaa7c3SKay Sievers static ssize_t disk_capability_show(struct device *dev,
764edfaa7c3SKay Sievers 				    struct device_attribute *attr, char *buf)
76586ce18d7SKristen Carlson Accardi {
766edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
767edfaa7c3SKay Sievers 
768edfaa7c3SKay Sievers 	return sprintf(buf, "%x\n", disk->flags);
76986ce18d7SKristen Carlson Accardi }
770edfaa7c3SKay Sievers 
771edfaa7c3SKay Sievers static ssize_t disk_stat_show(struct device *dev,
772edfaa7c3SKay Sievers 			      struct device_attribute *attr, char *buf)
7731da177e4SLinus Torvalds {
774edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
775c9959059STejun Heo 	int cpu;
776edfaa7c3SKay Sievers 
777c9959059STejun Heo 	cpu = disk_stat_lock();
778c9959059STejun Heo 	disk_round_stats(cpu, disk);
779c9959059STejun Heo 	disk_stat_unlock();
780edfaa7c3SKay Sievers 	return sprintf(buf,
781837c7878SBen Woodard 		"%8lu %8lu %8llu %8u "
782837c7878SBen Woodard 		"%8lu %8lu %8llu %8u "
7831da177e4SLinus Torvalds 		"%8u %8u %8u"
7841da177e4SLinus Torvalds 		"\n",
78547a00410SJens Axboe 		disk_stat_read(disk, ios[READ]),
78647a00410SJens Axboe 		disk_stat_read(disk, merges[READ]),
78747a00410SJens Axboe 		(unsigned long long)disk_stat_read(disk, sectors[READ]),
78847a00410SJens Axboe 		jiffies_to_msecs(disk_stat_read(disk, ticks[READ])),
78947a00410SJens Axboe 		disk_stat_read(disk, ios[WRITE]),
79047a00410SJens Axboe 		disk_stat_read(disk, merges[WRITE]),
79147a00410SJens Axboe 		(unsigned long long)disk_stat_read(disk, sectors[WRITE]),
79247a00410SJens Axboe 		jiffies_to_msecs(disk_stat_read(disk, ticks[WRITE])),
7931da177e4SLinus Torvalds 		disk->in_flight,
7941da177e4SLinus Torvalds 		jiffies_to_msecs(disk_stat_read(disk, io_ticks)),
7951da177e4SLinus Torvalds 		jiffies_to_msecs(disk_stat_read(disk, time_in_queue)));
7961da177e4SLinus Torvalds }
7971da177e4SLinus Torvalds 
798edfaa7c3SKay Sievers static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL);
7991f014290STejun Heo static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL);
800edfaa7c3SKay Sievers static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL);
8011c9ce527SKay Sievers static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL);
802e5610521STejun Heo static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL);
803edfaa7c3SKay Sievers static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL);
804edfaa7c3SKay Sievers static DEVICE_ATTR(stat, S_IRUGO, disk_stat_show, NULL);
805c17bb495SAkinobu Mita #ifdef CONFIG_FAIL_MAKE_REQUEST
806edfaa7c3SKay Sievers static struct device_attribute dev_attr_fail =
807*eddb2e26STejun Heo 	__ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store);
808c17bb495SAkinobu Mita #endif
809edfaa7c3SKay Sievers 
810edfaa7c3SKay Sievers static struct attribute *disk_attrs[] = {
811edfaa7c3SKay Sievers 	&dev_attr_range.attr,
8121f014290STejun Heo 	&dev_attr_ext_range.attr,
813edfaa7c3SKay Sievers 	&dev_attr_removable.attr,
8141c9ce527SKay Sievers 	&dev_attr_ro.attr,
815edfaa7c3SKay Sievers 	&dev_attr_size.attr,
816edfaa7c3SKay Sievers 	&dev_attr_capability.attr,
817edfaa7c3SKay Sievers 	&dev_attr_stat.attr,
818edfaa7c3SKay Sievers #ifdef CONFIG_FAIL_MAKE_REQUEST
819edfaa7c3SKay Sievers 	&dev_attr_fail.attr,
820edfaa7c3SKay Sievers #endif
821edfaa7c3SKay Sievers 	NULL
8221da177e4SLinus Torvalds };
8231da177e4SLinus Torvalds 
824edfaa7c3SKay Sievers static struct attribute_group disk_attr_group = {
825edfaa7c3SKay Sievers 	.attrs = disk_attrs,
826edfaa7c3SKay Sievers };
827edfaa7c3SKay Sievers 
828edfaa7c3SKay Sievers static struct attribute_group *disk_attr_groups[] = {
829edfaa7c3SKay Sievers 	&disk_attr_group,
830edfaa7c3SKay Sievers 	NULL
831edfaa7c3SKay Sievers };
832edfaa7c3SKay Sievers 
833edfaa7c3SKay Sievers static void disk_release(struct device *dev)
8341da177e4SLinus Torvalds {
835edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
836edfaa7c3SKay Sievers 
8371da177e4SLinus Torvalds 	kfree(disk->random);
838e71bf0d0STejun Heo 	kfree(disk->__part);
8391da177e4SLinus Torvalds 	free_disk_stats(disk);
8401da177e4SLinus Torvalds 	kfree(disk);
8411da177e4SLinus Torvalds }
842edfaa7c3SKay Sievers struct class block_class = {
843edfaa7c3SKay Sievers 	.name		= "block",
8441da177e4SLinus Torvalds };
8451da177e4SLinus Torvalds 
8461826eadfSAdrian Bunk static struct device_type disk_type = {
847edfaa7c3SKay Sievers 	.name		= "disk",
848edfaa7c3SKay Sievers 	.groups		= disk_attr_groups,
849edfaa7c3SKay Sievers 	.release	= disk_release,
8501da177e4SLinus Torvalds };
8511da177e4SLinus Torvalds 
852a6e2ba88SRandy Dunlap #ifdef CONFIG_PROC_FS
853cf771cb5STejun Heo /*
854cf771cb5STejun Heo  * aggregate disk stat collector.  Uses the same stats that the sysfs
855cf771cb5STejun Heo  * entries do, above, but makes them available through one seq_file.
856cf771cb5STejun Heo  *
857cf771cb5STejun Heo  * The output looks suspiciously like /proc/partitions with a bunch of
858cf771cb5STejun Heo  * extra fields.
859cf771cb5STejun Heo  */
860cf771cb5STejun Heo static int diskstats_show(struct seq_file *seqf, void *v)
8611da177e4SLinus Torvalds {
8621da177e4SLinus Torvalds 	struct gendisk *gp = v;
863e71bf0d0STejun Heo 	struct disk_part_iter piter;
864e71bf0d0STejun Heo 	struct hd_struct *hd;
8651da177e4SLinus Torvalds 	char buf[BDEVNAME_SIZE];
866c9959059STejun Heo 	int cpu;
8671da177e4SLinus Torvalds 
8681da177e4SLinus Torvalds 	/*
869ed9e1982STejun Heo 	if (&disk_to_dev(gp)->kobj.entry == block_class.devices.next)
870cf771cb5STejun Heo 		seq_puts(seqf,	"major minor name"
8711da177e4SLinus Torvalds 				"     rio rmerge rsect ruse wio wmerge "
8721da177e4SLinus Torvalds 				"wsect wuse running use aveq"
8731da177e4SLinus Torvalds 				"\n\n");
8741da177e4SLinus Torvalds 	*/
8751da177e4SLinus Torvalds 
876c9959059STejun Heo 	cpu = disk_stat_lock();
877c9959059STejun Heo 	disk_round_stats(cpu, gp);
878c9959059STejun Heo 	disk_stat_unlock();
8791f014290STejun Heo 	seq_printf(seqf, "%4d %7d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n",
880f331c029STejun Heo 		MAJOR(disk_devt(gp)), MINOR(disk_devt(gp)),
881f331c029STejun Heo 		disk_name(gp, 0, buf),
882a362357bSJens Axboe 		disk_stat_read(gp, ios[0]), disk_stat_read(gp, merges[0]),
883a362357bSJens Axboe 		(unsigned long long)disk_stat_read(gp, sectors[0]),
884a362357bSJens Axboe 		jiffies_to_msecs(disk_stat_read(gp, ticks[0])),
885a362357bSJens Axboe 		disk_stat_read(gp, ios[1]), disk_stat_read(gp, merges[1]),
886a362357bSJens Axboe 		(unsigned long long)disk_stat_read(gp, sectors[1]),
887a362357bSJens Axboe 		jiffies_to_msecs(disk_stat_read(gp, ticks[1])),
8881da177e4SLinus Torvalds 		gp->in_flight,
8891da177e4SLinus Torvalds 		jiffies_to_msecs(disk_stat_read(gp, io_ticks)),
8901da177e4SLinus Torvalds 		jiffies_to_msecs(disk_stat_read(gp, time_in_queue)));
8911da177e4SLinus Torvalds 
8921da177e4SLinus Torvalds 	/* now show all non-0 size partitions of it */
893e71bf0d0STejun Heo 	disk_part_iter_init(&piter, gp, 0);
894e71bf0d0STejun Heo 	while ((hd = disk_part_iter_next(&piter))) {
895c9959059STejun Heo 		cpu = disk_stat_lock();
896c9959059STejun Heo 		part_round_stats(cpu, hd);
897c9959059STejun Heo 		disk_stat_unlock();
8981f014290STejun Heo 		seq_printf(seqf, "%4d %7d %s %lu %lu %llu "
89928f39d55SJerome Marchand 			   "%u %lu %lu %llu %u %u %u %u\n",
900f331c029STejun Heo 			   MAJOR(part_devt(hd)), MINOR(part_devt(hd)),
901f331c029STejun Heo 			   disk_name(gp, hd->partno, buf),
90228f39d55SJerome Marchand 			   part_stat_read(hd, ios[0]),
90328f39d55SJerome Marchand 			   part_stat_read(hd, merges[0]),
90428f39d55SJerome Marchand 			   (unsigned long long)part_stat_read(hd, sectors[0]),
90528f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, ticks[0])),
90628f39d55SJerome Marchand 			   part_stat_read(hd, ios[1]),
90728f39d55SJerome Marchand 			   part_stat_read(hd, merges[1]),
90828f39d55SJerome Marchand 			   (unsigned long long)part_stat_read(hd, sectors[1]),
90928f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, ticks[1])),
91028f39d55SJerome Marchand 			   hd->in_flight,
91128f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, io_ticks)),
91228f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, time_in_queue))
91328f39d55SJerome Marchand 			);
9141da177e4SLinus Torvalds 	}
915e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
9161da177e4SLinus Torvalds 
9171da177e4SLinus Torvalds 	return 0;
9181da177e4SLinus Torvalds }
9191da177e4SLinus Torvalds 
92012f32bb3SJan Engelhardt const struct seq_operations diskstats_op = {
921def4e38dSTejun Heo 	.start	= disk_seqf_start,
922def4e38dSTejun Heo 	.next	= disk_seqf_next,
923def4e38dSTejun Heo 	.stop	= disk_seqf_stop,
9241da177e4SLinus Torvalds 	.show	= diskstats_show
9251da177e4SLinus Torvalds };
926a6e2ba88SRandy Dunlap #endif /* CONFIG_PROC_FS */
9271da177e4SLinus Torvalds 
9288ce7ad7bSKristen Carlson Accardi static void media_change_notify_thread(struct work_struct *work)
9298ce7ad7bSKristen Carlson Accardi {
9308ce7ad7bSKristen Carlson Accardi 	struct gendisk *gd = container_of(work, struct gendisk, async_notify);
9318ce7ad7bSKristen Carlson Accardi 	char event[] = "MEDIA_CHANGE=1";
9328ce7ad7bSKristen Carlson Accardi 	char *envp[] = { event, NULL };
9338ce7ad7bSKristen Carlson Accardi 
9348ce7ad7bSKristen Carlson Accardi 	/*
9358ce7ad7bSKristen Carlson Accardi 	 * set enviroment vars to indicate which event this is for
9368ce7ad7bSKristen Carlson Accardi 	 * so that user space will know to go check the media status.
9378ce7ad7bSKristen Carlson Accardi 	 */
938ed9e1982STejun Heo 	kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp);
9398ce7ad7bSKristen Carlson Accardi 	put_device(gd->driverfs_dev);
9408ce7ad7bSKristen Carlson Accardi }
9418ce7ad7bSKristen Carlson Accardi 
9421826eadfSAdrian Bunk #if 0
9438ce7ad7bSKristen Carlson Accardi void genhd_media_change_notify(struct gendisk *disk)
9448ce7ad7bSKristen Carlson Accardi {
9458ce7ad7bSKristen Carlson Accardi 	get_device(disk->driverfs_dev);
9468ce7ad7bSKristen Carlson Accardi 	schedule_work(&disk->async_notify);
9478ce7ad7bSKristen Carlson Accardi }
9488ce7ad7bSKristen Carlson Accardi EXPORT_SYMBOL_GPL(genhd_media_change_notify);
9491826eadfSAdrian Bunk #endif  /*  0  */
9508ce7ad7bSKristen Carlson Accardi 
951cf771cb5STejun Heo dev_t blk_lookup_devt(const char *name, int partno)
952edfaa7c3SKay Sievers {
953edfaa7c3SKay Sievers 	dev_t devt = MKDEV(0, 0);
954def4e38dSTejun Heo 	struct class_dev_iter iter;
955def4e38dSTejun Heo 	struct device *dev;
956edfaa7c3SKay Sievers 
957def4e38dSTejun Heo 	class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
958def4e38dSTejun Heo 	while ((dev = class_dev_iter_next(&iter))) {
959def4e38dSTejun Heo 		struct gendisk *disk = dev_to_disk(dev);
960548b10ebSTejun Heo 		struct hd_struct *part;
961def4e38dSTejun Heo 
962f331c029STejun Heo 		if (strcmp(dev->bus_id, name))
963f331c029STejun Heo 			continue;
964f331c029STejun Heo 
965e71bf0d0STejun Heo 		part = disk_get_part(disk, partno);
966548b10ebSTejun Heo 		if (part && (part->nr_sects || partno == 0)) {
967f331c029STejun Heo 			devt = part_devt(part);
968e71bf0d0STejun Heo 			disk_put_part(part);
969f331c029STejun Heo 			break;
970def4e38dSTejun Heo 		}
971548b10ebSTejun Heo 		disk_put_part(part);
972548b10ebSTejun Heo 	}
973def4e38dSTejun Heo 	class_dev_iter_exit(&iter);
974edfaa7c3SKay Sievers 	return devt;
975edfaa7c3SKay Sievers }
976edfaa7c3SKay Sievers EXPORT_SYMBOL(blk_lookup_devt);
977edfaa7c3SKay Sievers 
9781da177e4SLinus Torvalds struct gendisk *alloc_disk(int minors)
9791da177e4SLinus Torvalds {
9801946089aSChristoph Lameter 	return alloc_disk_node(minors, -1);
9811946089aSChristoph Lameter }
9821946089aSChristoph Lameter 
9831946089aSChristoph Lameter struct gendisk *alloc_disk_node(int minors, int node_id)
9841946089aSChristoph Lameter {
985bcce3de1STejun Heo 	return alloc_disk_ext_node(minors, 0, node_id);
986bcce3de1STejun Heo }
987bcce3de1STejun Heo 
988bcce3de1STejun Heo struct gendisk *alloc_disk_ext(int minors, int ext_minors)
989bcce3de1STejun Heo {
990bcce3de1STejun Heo 	return alloc_disk_ext_node(minors, ext_minors, -1);
991bcce3de1STejun Heo }
992bcce3de1STejun Heo 
993bcce3de1STejun Heo struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id)
994bcce3de1STejun Heo {
9951946089aSChristoph Lameter 	struct gendisk *disk;
9961946089aSChristoph Lameter 
99794f6030cSChristoph Lameter 	disk = kmalloc_node(sizeof(struct gendisk),
99894f6030cSChristoph Lameter 				GFP_KERNEL | __GFP_ZERO, node_id);
9991da177e4SLinus Torvalds 	if (disk) {
1000bcce3de1STejun Heo 		int tot_minors = minors + ext_minors;
1001b5d0b9dfSTejun Heo 		int size = tot_minors * sizeof(struct hd_struct *);
1002bcce3de1STejun Heo 
10031da177e4SLinus Torvalds 		if (!init_disk_stats(disk)) {
10041da177e4SLinus Torvalds 			kfree(disk);
10051da177e4SLinus Torvalds 			return NULL;
10061da177e4SLinus Torvalds 		}
1007b5d0b9dfSTejun Heo 
1008b5d0b9dfSTejun Heo 		disk->__part = kmalloc_node(size, GFP_KERNEL | __GFP_ZERO,
1009b5d0b9dfSTejun Heo 					    node_id);
1010e71bf0d0STejun Heo 		if (!disk->__part) {
1011c7674030SJerome Marchand 			free_disk_stats(disk);
10121da177e4SLinus Torvalds 			kfree(disk);
10131da177e4SLinus Torvalds 			return NULL;
10141da177e4SLinus Torvalds 		}
1015b5d0b9dfSTejun Heo 		disk->__part[0] = &disk->part0;
1016b5d0b9dfSTejun Heo 
10171da177e4SLinus Torvalds 		disk->minors = minors;
1018bcce3de1STejun Heo 		disk->ext_minors = ext_minors;
10191da177e4SLinus Torvalds 		rand_initialize_disk(disk);
1020ed9e1982STejun Heo 		disk_to_dev(disk)->class = &block_class;
1021ed9e1982STejun Heo 		disk_to_dev(disk)->type = &disk_type;
1022ed9e1982STejun Heo 		device_initialize(disk_to_dev(disk));
10238ce7ad7bSKristen Carlson Accardi 		INIT_WORK(&disk->async_notify,
10248ce7ad7bSKristen Carlson Accardi 			media_change_notify_thread);
10251da177e4SLinus Torvalds 	}
10261da177e4SLinus Torvalds 	return disk;
10271da177e4SLinus Torvalds }
10281da177e4SLinus Torvalds 
10291da177e4SLinus Torvalds EXPORT_SYMBOL(alloc_disk);
10301946089aSChristoph Lameter EXPORT_SYMBOL(alloc_disk_node);
1031bcce3de1STejun Heo EXPORT_SYMBOL(alloc_disk_ext);
1032bcce3de1STejun Heo EXPORT_SYMBOL(alloc_disk_ext_node);
10331da177e4SLinus Torvalds 
10341da177e4SLinus Torvalds struct kobject *get_disk(struct gendisk *disk)
10351da177e4SLinus Torvalds {
10361da177e4SLinus Torvalds 	struct module *owner;
10371da177e4SLinus Torvalds 	struct kobject *kobj;
10381da177e4SLinus Torvalds 
10391da177e4SLinus Torvalds 	if (!disk->fops)
10401da177e4SLinus Torvalds 		return NULL;
10411da177e4SLinus Torvalds 	owner = disk->fops->owner;
10421da177e4SLinus Torvalds 	if (owner && !try_module_get(owner))
10431da177e4SLinus Torvalds 		return NULL;
1044ed9e1982STejun Heo 	kobj = kobject_get(&disk_to_dev(disk)->kobj);
10451da177e4SLinus Torvalds 	if (kobj == NULL) {
10461da177e4SLinus Torvalds 		module_put(owner);
10471da177e4SLinus Torvalds 		return NULL;
10481da177e4SLinus Torvalds 	}
10491da177e4SLinus Torvalds 	return kobj;
10501da177e4SLinus Torvalds 
10511da177e4SLinus Torvalds }
10521da177e4SLinus Torvalds 
10531da177e4SLinus Torvalds EXPORT_SYMBOL(get_disk);
10541da177e4SLinus Torvalds 
10551da177e4SLinus Torvalds void put_disk(struct gendisk *disk)
10561da177e4SLinus Torvalds {
10571da177e4SLinus Torvalds 	if (disk)
1058ed9e1982STejun Heo 		kobject_put(&disk_to_dev(disk)->kobj);
10591da177e4SLinus Torvalds }
10601da177e4SLinus Torvalds 
10611da177e4SLinus Torvalds EXPORT_SYMBOL(put_disk);
10621da177e4SLinus Torvalds 
10631da177e4SLinus Torvalds void set_device_ro(struct block_device *bdev, int flag)
10641da177e4SLinus Torvalds {
10651da177e4SLinus Torvalds 	bdev->bd_part->policy = flag;
10661da177e4SLinus Torvalds }
10671da177e4SLinus Torvalds 
10681da177e4SLinus Torvalds EXPORT_SYMBOL(set_device_ro);
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds void set_disk_ro(struct gendisk *disk, int flag)
10711da177e4SLinus Torvalds {
1072e71bf0d0STejun Heo 	struct disk_part_iter piter;
1073e71bf0d0STejun Heo 	struct hd_struct *part;
1074e71bf0d0STejun Heo 
1075b7db9956STejun Heo 	disk_part_iter_init(&piter, disk,
1076b7db9956STejun Heo 			    DISK_PITER_INCL_EMPTY | DISK_PITER_INCL_PART0);
1077e71bf0d0STejun Heo 	while ((part = disk_part_iter_next(&piter)))
1078e71bf0d0STejun Heo 		part->policy = flag;
1079e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
10801da177e4SLinus Torvalds }
10811da177e4SLinus Torvalds 
10821da177e4SLinus Torvalds EXPORT_SYMBOL(set_disk_ro);
10831da177e4SLinus Torvalds 
10841da177e4SLinus Torvalds int bdev_read_only(struct block_device *bdev)
10851da177e4SLinus Torvalds {
10861da177e4SLinus Torvalds 	if (!bdev)
10871da177e4SLinus Torvalds 		return 0;
10881da177e4SLinus Torvalds 	return bdev->bd_part->policy;
10891da177e4SLinus Torvalds }
10901da177e4SLinus Torvalds 
10911da177e4SLinus Torvalds EXPORT_SYMBOL(bdev_read_only);
10921da177e4SLinus Torvalds 
1093cf771cb5STejun Heo int invalidate_partition(struct gendisk *disk, int partno)
10941da177e4SLinus Torvalds {
10951da177e4SLinus Torvalds 	int res = 0;
1096cf771cb5STejun Heo 	struct block_device *bdev = bdget_disk(disk, partno);
10971da177e4SLinus Torvalds 	if (bdev) {
10982ef41634SChristoph Hellwig 		fsync_bdev(bdev);
10992ef41634SChristoph Hellwig 		res = __invalidate_device(bdev);
11001da177e4SLinus Torvalds 		bdput(bdev);
11011da177e4SLinus Torvalds 	}
11021da177e4SLinus Torvalds 	return res;
11031da177e4SLinus Torvalds }
11041da177e4SLinus Torvalds 
11051da177e4SLinus Torvalds EXPORT_SYMBOL(invalidate_partition);
1106