xref: /linux/block/genhd.c (revision 2bbedcb4c1abac498f18e5770d62ae66ff235ada)
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 {
55540eed56STejun Heo 	struct hd_struct *part = NULL;
56540eed56STejun Heo 	struct disk_part_tbl *ptbl;
57e71bf0d0STejun Heo 
58540eed56STejun Heo 	if (unlikely(partno < 0))
59e71bf0d0STejun Heo 		return NULL;
60540eed56STejun Heo 
61e71bf0d0STejun Heo 	rcu_read_lock();
62540eed56STejun Heo 
63540eed56STejun Heo 	ptbl = rcu_dereference(disk->part_tbl);
64540eed56STejun Heo 	if (likely(partno < ptbl->len)) {
65540eed56STejun Heo 		part = rcu_dereference(ptbl->part[partno]);
66e71bf0d0STejun Heo 		if (part)
67ed9e1982STejun Heo 			get_device(part_to_dev(part));
68540eed56STejun Heo 	}
69540eed56STejun Heo 
70e71bf0d0STejun Heo 	rcu_read_unlock();
71e71bf0d0STejun Heo 
72e71bf0d0STejun Heo 	return part;
73e71bf0d0STejun Heo }
74e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_get_part);
75e71bf0d0STejun Heo 
76e71bf0d0STejun Heo /**
77e71bf0d0STejun Heo  * disk_part_iter_init - initialize partition iterator
78e71bf0d0STejun Heo  * @piter: iterator to initialize
79e71bf0d0STejun Heo  * @disk: disk to iterate over
80e71bf0d0STejun Heo  * @flags: DISK_PITER_* flags
81e71bf0d0STejun Heo  *
82e71bf0d0STejun Heo  * Initialize @piter so that it iterates over partitions of @disk.
83e71bf0d0STejun Heo  *
84e71bf0d0STejun Heo  * CONTEXT:
85e71bf0d0STejun Heo  * Don't care.
86e71bf0d0STejun Heo  */
87e71bf0d0STejun Heo void disk_part_iter_init(struct disk_part_iter *piter, struct gendisk *disk,
88e71bf0d0STejun Heo 			  unsigned int flags)
89e71bf0d0STejun Heo {
90540eed56STejun Heo 	struct disk_part_tbl *ptbl;
91540eed56STejun Heo 
92540eed56STejun Heo 	rcu_read_lock();
93540eed56STejun Heo 	ptbl = rcu_dereference(disk->part_tbl);
94540eed56STejun Heo 
95e71bf0d0STejun Heo 	piter->disk = disk;
96e71bf0d0STejun Heo 	piter->part = NULL;
97e71bf0d0STejun Heo 
98e71bf0d0STejun Heo 	if (flags & DISK_PITER_REVERSE)
99540eed56STejun Heo 		piter->idx = ptbl->len - 1;
100b5d0b9dfSTejun Heo 	else if (flags & DISK_PITER_INCL_PART0)
101e71bf0d0STejun Heo 		piter->idx = 0;
102b5d0b9dfSTejun Heo 	else
103b5d0b9dfSTejun Heo 		piter->idx = 1;
104e71bf0d0STejun Heo 
105e71bf0d0STejun Heo 	piter->flags = flags;
106540eed56STejun Heo 
107540eed56STejun Heo 	rcu_read_unlock();
108e71bf0d0STejun Heo }
109e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_part_iter_init);
110e71bf0d0STejun Heo 
111e71bf0d0STejun Heo /**
112e71bf0d0STejun Heo  * disk_part_iter_next - proceed iterator to the next partition and return it
113e71bf0d0STejun Heo  * @piter: iterator of interest
114e71bf0d0STejun Heo  *
115e71bf0d0STejun Heo  * Proceed @piter to the next partition and return it.
116e71bf0d0STejun Heo  *
117e71bf0d0STejun Heo  * CONTEXT:
118e71bf0d0STejun Heo  * Don't care.
119e71bf0d0STejun Heo  */
120e71bf0d0STejun Heo struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter)
121e71bf0d0STejun Heo {
122540eed56STejun Heo 	struct disk_part_tbl *ptbl;
123e71bf0d0STejun Heo 	int inc, end;
124e71bf0d0STejun Heo 
125e71bf0d0STejun Heo 	/* put the last partition */
126e71bf0d0STejun Heo 	disk_put_part(piter->part);
127e71bf0d0STejun Heo 	piter->part = NULL;
128e71bf0d0STejun Heo 
129540eed56STejun Heo 	/* get part_tbl */
130e71bf0d0STejun Heo 	rcu_read_lock();
131540eed56STejun Heo 	ptbl = rcu_dereference(piter->disk->part_tbl);
132e71bf0d0STejun Heo 
133e71bf0d0STejun Heo 	/* determine iteration parameters */
134e71bf0d0STejun Heo 	if (piter->flags & DISK_PITER_REVERSE) {
135e71bf0d0STejun Heo 		inc = -1;
136b5d0b9dfSTejun Heo 		if (piter->flags & DISK_PITER_INCL_PART0)
137e71bf0d0STejun Heo 			end = -1;
138b5d0b9dfSTejun Heo 		else
139b5d0b9dfSTejun Heo 			end = 0;
140e71bf0d0STejun Heo 	} else {
141e71bf0d0STejun Heo 		inc = 1;
142540eed56STejun Heo 		end = ptbl->len;
143e71bf0d0STejun Heo 	}
144e71bf0d0STejun Heo 
145e71bf0d0STejun Heo 	/* iterate to the next partition */
146e71bf0d0STejun Heo 	for (; piter->idx != end; piter->idx += inc) {
147e71bf0d0STejun Heo 		struct hd_struct *part;
148e71bf0d0STejun Heo 
149540eed56STejun Heo 		part = rcu_dereference(ptbl->part[piter->idx]);
150e71bf0d0STejun Heo 		if (!part)
151e71bf0d0STejun Heo 			continue;
152e71bf0d0STejun Heo 		if (!(piter->flags & DISK_PITER_INCL_EMPTY) && !part->nr_sects)
153e71bf0d0STejun Heo 			continue;
154e71bf0d0STejun Heo 
155ed9e1982STejun Heo 		get_device(part_to_dev(part));
156e71bf0d0STejun Heo 		piter->part = part;
157e71bf0d0STejun Heo 		piter->idx += inc;
158e71bf0d0STejun Heo 		break;
159e71bf0d0STejun Heo 	}
160e71bf0d0STejun Heo 
161e71bf0d0STejun Heo 	rcu_read_unlock();
162e71bf0d0STejun Heo 
163e71bf0d0STejun Heo 	return piter->part;
164e71bf0d0STejun Heo }
165e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_part_iter_next);
166e71bf0d0STejun Heo 
167e71bf0d0STejun Heo /**
168e71bf0d0STejun Heo  * disk_part_iter_exit - finish up partition iteration
169e71bf0d0STejun Heo  * @piter: iter of interest
170e71bf0d0STejun Heo  *
171e71bf0d0STejun Heo  * Called when iteration is over.  Cleans up @piter.
172e71bf0d0STejun Heo  *
173e71bf0d0STejun Heo  * CONTEXT:
174e71bf0d0STejun Heo  * Don't care.
175e71bf0d0STejun Heo  */
176e71bf0d0STejun Heo void disk_part_iter_exit(struct disk_part_iter *piter)
177e71bf0d0STejun Heo {
178e71bf0d0STejun Heo 	disk_put_part(piter->part);
179e71bf0d0STejun Heo 	piter->part = NULL;
180e71bf0d0STejun Heo }
181e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_part_iter_exit);
182e71bf0d0STejun Heo 
183e71bf0d0STejun Heo /**
184e71bf0d0STejun Heo  * disk_map_sector_rcu - map sector to partition
185e71bf0d0STejun Heo  * @disk: gendisk of interest
186e71bf0d0STejun Heo  * @sector: sector to map
187e71bf0d0STejun Heo  *
188e71bf0d0STejun Heo  * Find out which partition @sector maps to on @disk.  This is
189e71bf0d0STejun Heo  * primarily used for stats accounting.
190e71bf0d0STejun Heo  *
191e71bf0d0STejun Heo  * CONTEXT:
192e71bf0d0STejun Heo  * RCU read locked.  The returned partition pointer is valid only
193e71bf0d0STejun Heo  * while preemption is disabled.
194e71bf0d0STejun Heo  *
195e71bf0d0STejun Heo  * RETURNS:
196074a7acaSTejun Heo  * Found partition on success, part0 is returned if no partition matches
197e71bf0d0STejun Heo  */
198e71bf0d0STejun Heo struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector)
199e71bf0d0STejun Heo {
200540eed56STejun Heo 	struct disk_part_tbl *ptbl;
201e71bf0d0STejun Heo 	int i;
202e71bf0d0STejun Heo 
203540eed56STejun Heo 	ptbl = rcu_dereference(disk->part_tbl);
204540eed56STejun Heo 
205540eed56STejun Heo 	for (i = 1; i < ptbl->len; i++) {
206540eed56STejun Heo 		struct hd_struct *part = rcu_dereference(ptbl->part[i]);
207e71bf0d0STejun Heo 
208e71bf0d0STejun Heo 		if (part && part->start_sect <= sector &&
209e71bf0d0STejun Heo 		    sector < part->start_sect + part->nr_sects)
210e71bf0d0STejun Heo 			return part;
211e71bf0d0STejun Heo 	}
212074a7acaSTejun Heo 	return &disk->part0;
213e71bf0d0STejun Heo }
214e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_map_sector_rcu);
215e71bf0d0STejun Heo 
2161da177e4SLinus Torvalds /*
2171da177e4SLinus Torvalds  * Can be deleted altogether. Later.
2181da177e4SLinus Torvalds  *
2191da177e4SLinus Torvalds  */
2201da177e4SLinus Torvalds static struct blk_major_name {
2211da177e4SLinus Torvalds 	struct blk_major_name *next;
2221da177e4SLinus Torvalds 	int major;
2231da177e4SLinus Torvalds 	char name[16];
22468eef3b4SJoe Korty } *major_names[BLKDEV_MAJOR_HASH_SIZE];
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds /* index in the above - for now: assume no multimajor ranges */
2271da177e4SLinus Torvalds static inline int major_to_index(int major)
2281da177e4SLinus Torvalds {
22968eef3b4SJoe Korty 	return major % BLKDEV_MAJOR_HASH_SIZE;
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds 
23268eef3b4SJoe Korty #ifdef CONFIG_PROC_FS
233cf771cb5STejun Heo void blkdev_show(struct seq_file *seqf, off_t offset)
2347170be5fSNeil Horman {
23568eef3b4SJoe Korty 	struct blk_major_name *dp;
2367170be5fSNeil Horman 
23768eef3b4SJoe Korty 	if (offset < BLKDEV_MAJOR_HASH_SIZE) {
238edfaa7c3SKay Sievers 		mutex_lock(&block_class_lock);
23968eef3b4SJoe Korty 		for (dp = major_names[offset]; dp; dp = dp->next)
240cf771cb5STejun Heo 			seq_printf(seqf, "%3d %s\n", dp->major, dp->name);
241edfaa7c3SKay Sievers 		mutex_unlock(&block_class_lock);
24268eef3b4SJoe Korty 	}
2437170be5fSNeil Horman }
24468eef3b4SJoe Korty #endif /* CONFIG_PROC_FS */
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds int register_blkdev(unsigned int major, const char *name)
2471da177e4SLinus Torvalds {
2481da177e4SLinus Torvalds 	struct blk_major_name **n, *p;
2491da177e4SLinus Torvalds 	int index, ret = 0;
2501da177e4SLinus Torvalds 
251edfaa7c3SKay Sievers 	mutex_lock(&block_class_lock);
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds 	/* temporary */
2541da177e4SLinus Torvalds 	if (major == 0) {
2551da177e4SLinus Torvalds 		for (index = ARRAY_SIZE(major_names)-1; index > 0; index--) {
2561da177e4SLinus Torvalds 			if (major_names[index] == NULL)
2571da177e4SLinus Torvalds 				break;
2581da177e4SLinus Torvalds 		}
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds 		if (index == 0) {
2611da177e4SLinus Torvalds 			printk("register_blkdev: failed to get major for %s\n",
2621da177e4SLinus Torvalds 			       name);
2631da177e4SLinus Torvalds 			ret = -EBUSY;
2641da177e4SLinus Torvalds 			goto out;
2651da177e4SLinus Torvalds 		}
2661da177e4SLinus Torvalds 		major = index;
2671da177e4SLinus Torvalds 		ret = major;
2681da177e4SLinus Torvalds 	}
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds 	p = kmalloc(sizeof(struct blk_major_name), GFP_KERNEL);
2711da177e4SLinus Torvalds 	if (p == NULL) {
2721da177e4SLinus Torvalds 		ret = -ENOMEM;
2731da177e4SLinus Torvalds 		goto out;
2741da177e4SLinus Torvalds 	}
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 	p->major = major;
2771da177e4SLinus Torvalds 	strlcpy(p->name, name, sizeof(p->name));
2781da177e4SLinus Torvalds 	p->next = NULL;
2791da177e4SLinus Torvalds 	index = major_to_index(major);
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 	for (n = &major_names[index]; *n; n = &(*n)->next) {
2821da177e4SLinus Torvalds 		if ((*n)->major == major)
2831da177e4SLinus Torvalds 			break;
2841da177e4SLinus Torvalds 	}
2851da177e4SLinus Torvalds 	if (!*n)
2861da177e4SLinus Torvalds 		*n = p;
2871da177e4SLinus Torvalds 	else
2881da177e4SLinus Torvalds 		ret = -EBUSY;
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds 	if (ret < 0) {
2911da177e4SLinus Torvalds 		printk("register_blkdev: cannot get major %d for %s\n",
2921da177e4SLinus Torvalds 		       major, name);
2931da177e4SLinus Torvalds 		kfree(p);
2941da177e4SLinus Torvalds 	}
2951da177e4SLinus Torvalds out:
296edfaa7c3SKay Sievers 	mutex_unlock(&block_class_lock);
2971da177e4SLinus Torvalds 	return ret;
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds EXPORT_SYMBOL(register_blkdev);
3011da177e4SLinus Torvalds 
302f4480240SAkinobu Mita void unregister_blkdev(unsigned int major, const char *name)
3031da177e4SLinus Torvalds {
3041da177e4SLinus Torvalds 	struct blk_major_name **n;
3051da177e4SLinus Torvalds 	struct blk_major_name *p = NULL;
3061da177e4SLinus Torvalds 	int index = major_to_index(major);
3071da177e4SLinus Torvalds 
308edfaa7c3SKay Sievers 	mutex_lock(&block_class_lock);
3091da177e4SLinus Torvalds 	for (n = &major_names[index]; *n; n = &(*n)->next)
3101da177e4SLinus Torvalds 		if ((*n)->major == major)
3111da177e4SLinus Torvalds 			break;
312294462a5SAkinobu Mita 	if (!*n || strcmp((*n)->name, name)) {
313294462a5SAkinobu Mita 		WARN_ON(1);
314294462a5SAkinobu Mita 	} else {
3151da177e4SLinus Torvalds 		p = *n;
3161da177e4SLinus Torvalds 		*n = p->next;
3171da177e4SLinus Torvalds 	}
318edfaa7c3SKay Sievers 	mutex_unlock(&block_class_lock);
3191da177e4SLinus Torvalds 	kfree(p);
3201da177e4SLinus Torvalds }
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds EXPORT_SYMBOL(unregister_blkdev);
3231da177e4SLinus Torvalds 
3241da177e4SLinus Torvalds static struct kobj_map *bdev_map;
3251da177e4SLinus Torvalds 
326bcce3de1STejun Heo /**
327870d6656STejun Heo  * blk_mangle_minor - scatter minor numbers apart
328870d6656STejun Heo  * @minor: minor number to mangle
329870d6656STejun Heo  *
330870d6656STejun Heo  * Scatter consecutively allocated @minor number apart if MANGLE_DEVT
331870d6656STejun Heo  * is enabled.  Mangling twice gives the original value.
332870d6656STejun Heo  *
333870d6656STejun Heo  * RETURNS:
334870d6656STejun Heo  * Mangled value.
335870d6656STejun Heo  *
336870d6656STejun Heo  * CONTEXT:
337870d6656STejun Heo  * Don't care.
338870d6656STejun Heo  */
339870d6656STejun Heo static int blk_mangle_minor(int minor)
340870d6656STejun Heo {
341870d6656STejun Heo #ifdef CONFIG_DEBUG_BLOCK_EXT_DEVT
342870d6656STejun Heo 	int i;
343870d6656STejun Heo 
344870d6656STejun Heo 	for (i = 0; i < MINORBITS / 2; i++) {
345870d6656STejun Heo 		int low = minor & (1 << i);
346870d6656STejun Heo 		int high = minor & (1 << (MINORBITS - 1 - i));
347870d6656STejun Heo 		int distance = MINORBITS - 1 - 2 * i;
348870d6656STejun Heo 
349870d6656STejun Heo 		minor ^= low | high;	/* clear both bits */
350870d6656STejun Heo 		low <<= distance;	/* swap the positions */
351870d6656STejun Heo 		high >>= distance;
352870d6656STejun Heo 		minor |= low | high;	/* and set */
353870d6656STejun Heo 	}
354870d6656STejun Heo #endif
355870d6656STejun Heo 	return minor;
356870d6656STejun Heo }
357870d6656STejun Heo 
358870d6656STejun Heo /**
359bcce3de1STejun Heo  * blk_alloc_devt - allocate a dev_t for a partition
360bcce3de1STejun Heo  * @part: partition to allocate dev_t for
361bcce3de1STejun Heo  * @gfp_mask: memory allocation flag
362bcce3de1STejun Heo  * @devt: out parameter for resulting dev_t
363bcce3de1STejun Heo  *
364bcce3de1STejun Heo  * Allocate a dev_t for block device.
365bcce3de1STejun Heo  *
366bcce3de1STejun Heo  * RETURNS:
367bcce3de1STejun Heo  * 0 on success, allocated dev_t is returned in *@devt.  -errno on
368bcce3de1STejun Heo  * failure.
369bcce3de1STejun Heo  *
370bcce3de1STejun Heo  * CONTEXT:
371bcce3de1STejun Heo  * Might sleep.
372bcce3de1STejun Heo  */
373bcce3de1STejun Heo int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
374bcce3de1STejun Heo {
375bcce3de1STejun Heo 	struct gendisk *disk = part_to_disk(part);
376bcce3de1STejun Heo 	int idx, rc;
377bcce3de1STejun Heo 
378bcce3de1STejun Heo 	/* in consecutive minor range? */
379bcce3de1STejun Heo 	if (part->partno < disk->minors) {
380bcce3de1STejun Heo 		*devt = MKDEV(disk->major, disk->first_minor + part->partno);
381bcce3de1STejun Heo 		return 0;
382bcce3de1STejun Heo 	}
383bcce3de1STejun Heo 
384bcce3de1STejun Heo 	/* allocate ext devt */
385bcce3de1STejun Heo 	do {
386bcce3de1STejun Heo 		if (!idr_pre_get(&ext_devt_idr, GFP_KERNEL))
387bcce3de1STejun Heo 			return -ENOMEM;
388bcce3de1STejun Heo 		rc = idr_get_new(&ext_devt_idr, part, &idx);
389bcce3de1STejun Heo 	} while (rc == -EAGAIN);
390bcce3de1STejun Heo 
391bcce3de1STejun Heo 	if (rc)
392bcce3de1STejun Heo 		return rc;
393bcce3de1STejun Heo 
394bcce3de1STejun Heo 	if (idx > MAX_EXT_DEVT) {
395bcce3de1STejun Heo 		idr_remove(&ext_devt_idr, idx);
396bcce3de1STejun Heo 		return -EBUSY;
397bcce3de1STejun Heo 	}
398bcce3de1STejun Heo 
399870d6656STejun Heo 	*devt = MKDEV(BLOCK_EXT_MAJOR, blk_mangle_minor(idx));
400bcce3de1STejun Heo 	return 0;
401bcce3de1STejun Heo }
402bcce3de1STejun Heo 
403bcce3de1STejun Heo /**
404bcce3de1STejun Heo  * blk_free_devt - free a dev_t
405bcce3de1STejun Heo  * @devt: dev_t to free
406bcce3de1STejun Heo  *
407bcce3de1STejun Heo  * Free @devt which was allocated using blk_alloc_devt().
408bcce3de1STejun Heo  *
409bcce3de1STejun Heo  * CONTEXT:
410bcce3de1STejun Heo  * Might sleep.
411bcce3de1STejun Heo  */
412bcce3de1STejun Heo void blk_free_devt(dev_t devt)
413bcce3de1STejun Heo {
414bcce3de1STejun Heo 	might_sleep();
415bcce3de1STejun Heo 
416bcce3de1STejun Heo 	if (devt == MKDEV(0, 0))
417bcce3de1STejun Heo 		return;
418bcce3de1STejun Heo 
419bcce3de1STejun Heo 	if (MAJOR(devt) == BLOCK_EXT_MAJOR) {
420bcce3de1STejun Heo 		mutex_lock(&ext_devt_mutex);
421870d6656STejun Heo 		idr_remove(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
422bcce3de1STejun Heo 		mutex_unlock(&ext_devt_mutex);
423bcce3de1STejun Heo 	}
424bcce3de1STejun Heo }
425bcce3de1STejun Heo 
4261f014290STejun Heo static char *bdevt_str(dev_t devt, char *buf)
4271f014290STejun Heo {
4281f014290STejun Heo 	if (MAJOR(devt) <= 0xff && MINOR(devt) <= 0xff) {
4291f014290STejun Heo 		char tbuf[BDEVT_SIZE];
4301f014290STejun Heo 		snprintf(tbuf, BDEVT_SIZE, "%02x%02x", MAJOR(devt), MINOR(devt));
4311f014290STejun Heo 		snprintf(buf, BDEVT_SIZE, "%-9s", tbuf);
4321f014290STejun Heo 	} else
4331f014290STejun Heo 		snprintf(buf, BDEVT_SIZE, "%03x:%05x", MAJOR(devt), MINOR(devt));
4341f014290STejun Heo 
4351f014290STejun Heo 	return buf;
4361f014290STejun Heo }
4371f014290STejun Heo 
4381da177e4SLinus Torvalds /*
4391da177e4SLinus Torvalds  * Register device numbers dev..(dev+range-1)
4401da177e4SLinus Torvalds  * range must be nonzero
4411da177e4SLinus Torvalds  * The hash chain is sorted on range, so that subranges can override.
4421da177e4SLinus Torvalds  */
443edfaa7c3SKay Sievers void blk_register_region(dev_t devt, unsigned long range, struct module *module,
4441da177e4SLinus Torvalds 			 struct kobject *(*probe)(dev_t, int *, void *),
4451da177e4SLinus Torvalds 			 int (*lock)(dev_t, void *), void *data)
4461da177e4SLinus Torvalds {
447edfaa7c3SKay Sievers 	kobj_map(bdev_map, devt, range, module, probe, lock, data);
4481da177e4SLinus Torvalds }
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds EXPORT_SYMBOL(blk_register_region);
4511da177e4SLinus Torvalds 
452edfaa7c3SKay Sievers void blk_unregister_region(dev_t devt, unsigned long range)
4531da177e4SLinus Torvalds {
454edfaa7c3SKay Sievers 	kobj_unmap(bdev_map, devt, range);
4551da177e4SLinus Torvalds }
4561da177e4SLinus Torvalds 
4571da177e4SLinus Torvalds EXPORT_SYMBOL(blk_unregister_region);
4581da177e4SLinus Torvalds 
459cf771cb5STejun Heo static struct kobject *exact_match(dev_t devt, int *partno, void *data)
4601da177e4SLinus Torvalds {
4611da177e4SLinus Torvalds 	struct gendisk *p = data;
462edfaa7c3SKay Sievers 
463ed9e1982STejun Heo 	return &disk_to_dev(p)->kobj;
4641da177e4SLinus Torvalds }
4651da177e4SLinus Torvalds 
466edfaa7c3SKay Sievers static int exact_lock(dev_t devt, void *data)
4671da177e4SLinus Torvalds {
4681da177e4SLinus Torvalds 	struct gendisk *p = data;
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds 	if (!get_disk(p))
4711da177e4SLinus Torvalds 		return -1;
4721da177e4SLinus Torvalds 	return 0;
4731da177e4SLinus Torvalds }
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds /**
4761da177e4SLinus Torvalds  * add_disk - add partitioning information to kernel list
4771da177e4SLinus Torvalds  * @disk: per-device partitioning information
4781da177e4SLinus Torvalds  *
4791da177e4SLinus Torvalds  * This function registers the partitioning information in @disk
4801da177e4SLinus Torvalds  * with the kernel.
4813e1a7ff8STejun Heo  *
4823e1a7ff8STejun Heo  * FIXME: error handling
4831da177e4SLinus Torvalds  */
4841da177e4SLinus Torvalds void add_disk(struct gendisk *disk)
4851da177e4SLinus Torvalds {
486cf0ca9feSPeter Zijlstra 	struct backing_dev_info *bdi;
4873e1a7ff8STejun Heo 	dev_t devt;
4886ffeea77SGreg Kroah-Hartman 	int retval;
489cf0ca9feSPeter Zijlstra 
4903e1a7ff8STejun Heo 	/* minors == 0 indicates to use ext devt from part0 and should
4913e1a7ff8STejun Heo 	 * be accompanied with EXT_DEVT flag.  Make sure all
4923e1a7ff8STejun Heo 	 * parameters make sense.
4933e1a7ff8STejun Heo 	 */
4943e1a7ff8STejun Heo 	WARN_ON(disk->minors && !(disk->major || disk->first_minor));
4953e1a7ff8STejun Heo 	WARN_ON(!disk->minors && !(disk->flags & GENHD_FL_EXT_DEVT));
4963e1a7ff8STejun Heo 
4971da177e4SLinus Torvalds 	disk->flags |= GENHD_FL_UP;
4983e1a7ff8STejun Heo 
4993e1a7ff8STejun Heo 	retval = blk_alloc_devt(&disk->part0, &devt);
5003e1a7ff8STejun Heo 	if (retval) {
5013e1a7ff8STejun Heo 		WARN_ON(1);
5023e1a7ff8STejun Heo 		return;
5033e1a7ff8STejun Heo 	}
5043e1a7ff8STejun Heo 	disk_to_dev(disk)->devt = devt;
5053e1a7ff8STejun Heo 
5063e1a7ff8STejun Heo 	/* ->major and ->first_minor aren't supposed to be
5073e1a7ff8STejun Heo 	 * dereferenced from here on, but set them just in case.
5083e1a7ff8STejun Heo 	 */
5093e1a7ff8STejun Heo 	disk->major = MAJOR(devt);
5103e1a7ff8STejun Heo 	disk->first_minor = MINOR(devt);
5113e1a7ff8STejun Heo 
512f331c029STejun Heo 	blk_register_region(disk_devt(disk), disk->minors, NULL,
513f331c029STejun Heo 			    exact_match, exact_lock, disk);
5141da177e4SLinus Torvalds 	register_disk(disk);
5151da177e4SLinus Torvalds 	blk_register_queue(disk);
516cf0ca9feSPeter Zijlstra 
517cf0ca9feSPeter Zijlstra 	bdi = &disk->queue->backing_dev_info;
518f331c029STejun Heo 	bdi_register_dev(bdi, disk_devt(disk));
519ed9e1982STejun Heo 	retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj,
520ed9e1982STejun Heo 				   "bdi");
5216ffeea77SGreg Kroah-Hartman 	WARN_ON(retval);
5221da177e4SLinus Torvalds }
5231da177e4SLinus Torvalds 
5241da177e4SLinus Torvalds EXPORT_SYMBOL(add_disk);
5251da177e4SLinus Torvalds EXPORT_SYMBOL(del_gendisk);	/* in partitions/check.c */
5261da177e4SLinus Torvalds 
5271da177e4SLinus Torvalds void unlink_gendisk(struct gendisk *disk)
5281da177e4SLinus Torvalds {
529ed9e1982STejun Heo 	sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi");
530cf0ca9feSPeter Zijlstra 	bdi_unregister(&disk->queue->backing_dev_info);
5311da177e4SLinus Torvalds 	blk_unregister_queue(disk);
532f331c029STejun Heo 	blk_unregister_region(disk_devt(disk), disk->minors);
5331da177e4SLinus Torvalds }
5341da177e4SLinus Torvalds 
5351da177e4SLinus Torvalds /**
5361da177e4SLinus Torvalds  * get_gendisk - get partitioning information for a given device
537710027a4SRandy Dunlap  * @devt: device to get partitioning information for
538710027a4SRandy Dunlap  * @part: returned partition index
5391da177e4SLinus Torvalds  *
5401da177e4SLinus Torvalds  * This function gets the structure containing partitioning
541710027a4SRandy Dunlap  * information for the given device @devt.
5421da177e4SLinus Torvalds  */
543cf771cb5STejun Heo struct gendisk *get_gendisk(dev_t devt, int *partno)
5441da177e4SLinus Torvalds {
545bcce3de1STejun Heo 	struct gendisk *disk = NULL;
546edfaa7c3SKay Sievers 
547bcce3de1STejun Heo 	if (MAJOR(devt) != BLOCK_EXT_MAJOR) {
548bcce3de1STejun Heo 		struct kobject *kobj;
549bcce3de1STejun Heo 
550bcce3de1STejun Heo 		kobj = kobj_lookup(bdev_map, devt, partno);
551bcce3de1STejun Heo 		if (kobj)
552bcce3de1STejun Heo 			disk = dev_to_disk(kobj_to_dev(kobj));
553bcce3de1STejun Heo 	} else {
554bcce3de1STejun Heo 		struct hd_struct *part;
555bcce3de1STejun Heo 
556bcce3de1STejun Heo 		mutex_lock(&ext_devt_mutex);
557870d6656STejun Heo 		part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
558bcce3de1STejun Heo 		if (part && get_disk(part_to_disk(part))) {
559bcce3de1STejun Heo 			*partno = part->partno;
560bcce3de1STejun Heo 			disk = part_to_disk(part);
561bcce3de1STejun Heo 		}
562bcce3de1STejun Heo 		mutex_unlock(&ext_devt_mutex);
563bcce3de1STejun Heo 	}
564bcce3de1STejun Heo 
565bcce3de1STejun Heo 	return disk;
5661da177e4SLinus Torvalds }
5671da177e4SLinus Torvalds 
568f331c029STejun Heo /**
569f331c029STejun Heo  * bdget_disk - do bdget() by gendisk and partition number
570f331c029STejun Heo  * @disk: gendisk of interest
571f331c029STejun Heo  * @partno: partition number
572f331c029STejun Heo  *
573f331c029STejun Heo  * Find partition @partno from @disk, do bdget() on it.
574f331c029STejun Heo  *
575f331c029STejun Heo  * CONTEXT:
576f331c029STejun Heo  * Don't care.
577f331c029STejun Heo  *
578f331c029STejun Heo  * RETURNS:
579f331c029STejun Heo  * Resulting block_device on success, NULL on failure.
580f331c029STejun Heo  */
581aeb3d3a8SHarvey Harrison struct block_device *bdget_disk(struct gendisk *disk, int partno)
582f331c029STejun Heo {
583e71bf0d0STejun Heo 	struct hd_struct *part;
584548b10ebSTejun Heo 	struct block_device *bdev = NULL;
585f331c029STejun Heo 
586e71bf0d0STejun Heo 	part = disk_get_part(disk, partno);
587*2bbedcb4STejun Heo 	if (part)
588548b10ebSTejun Heo 		bdev = bdget(part_devt(part));
589e71bf0d0STejun Heo 	disk_put_part(part);
590f331c029STejun Heo 
591548b10ebSTejun Heo 	return bdev;
592f331c029STejun Heo }
593f331c029STejun Heo EXPORT_SYMBOL(bdget_disk);
594f331c029STejun Heo 
595dd2a345fSDave Gilbert /*
5965c6f35c5SGreg Kroah-Hartman  * print a full list of all partitions - intended for places where the root
5975c6f35c5SGreg Kroah-Hartman  * filesystem can't be mounted and thus to give the victim some idea of what
5985c6f35c5SGreg Kroah-Hartman  * went wrong
5995c6f35c5SGreg Kroah-Hartman  */
6005c6f35c5SGreg Kroah-Hartman void __init printk_all_partitions(void)
6015c6f35c5SGreg Kroah-Hartman {
602def4e38dSTejun Heo 	struct class_dev_iter iter;
603def4e38dSTejun Heo 	struct device *dev;
604def4e38dSTejun Heo 
605def4e38dSTejun Heo 	class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
606def4e38dSTejun Heo 	while ((dev = class_dev_iter_next(&iter))) {
607def4e38dSTejun Heo 		struct gendisk *disk = dev_to_disk(dev);
608e71bf0d0STejun Heo 		struct disk_part_iter piter;
609e71bf0d0STejun Heo 		struct hd_struct *part;
6101f014290STejun Heo 		char name_buf[BDEVNAME_SIZE];
6111f014290STejun Heo 		char devt_buf[BDEVT_SIZE];
612def4e38dSTejun Heo 
613def4e38dSTejun Heo 		/*
614def4e38dSTejun Heo 		 * Don't show empty devices or things that have been
615def4e38dSTejun Heo 		 * surpressed
616def4e38dSTejun Heo 		 */
617def4e38dSTejun Heo 		if (get_capacity(disk) == 0 ||
618def4e38dSTejun Heo 		    (disk->flags & GENHD_FL_SUPPRESS_PARTITION_INFO))
619def4e38dSTejun Heo 			continue;
620def4e38dSTejun Heo 
621def4e38dSTejun Heo 		/*
622def4e38dSTejun Heo 		 * Note, unlike /proc/partitions, I am showing the
623def4e38dSTejun Heo 		 * numbers in hex - the same format as the root=
624def4e38dSTejun Heo 		 * option takes.
625def4e38dSTejun Heo 		 */
626074a7acaSTejun Heo 		disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0);
627074a7acaSTejun Heo 		while ((part = disk_part_iter_next(&piter))) {
628074a7acaSTejun Heo 			bool is_part0 = part == &disk->part0;
629074a7acaSTejun Heo 
630074a7acaSTejun Heo 			printk("%s%s %10llu %s", is_part0 ? "" : "  ",
631074a7acaSTejun Heo 			       bdevt_str(part_devt(part), devt_buf),
632074a7acaSTejun Heo 			       (unsigned long long)part->nr_sects >> 1,
633074a7acaSTejun Heo 			       disk_name(disk, part->partno, name_buf));
634074a7acaSTejun Heo 			if (is_part0) {
635def4e38dSTejun Heo 				if (disk->driverfs_dev != NULL &&
636def4e38dSTejun Heo 				    disk->driverfs_dev->driver != NULL)
637def4e38dSTejun Heo 					printk(" driver: %s\n",
638def4e38dSTejun Heo 					      disk->driverfs_dev->driver->name);
639def4e38dSTejun Heo 				else
640def4e38dSTejun Heo 					printk(" (driver?)\n");
641074a7acaSTejun Heo 			} else
642074a7acaSTejun Heo 				printk("\n");
643074a7acaSTejun Heo 		}
644e71bf0d0STejun Heo 		disk_part_iter_exit(&piter);
645def4e38dSTejun Heo 	}
646def4e38dSTejun Heo 	class_dev_iter_exit(&iter);
647dd2a345fSDave Gilbert }
648dd2a345fSDave Gilbert 
6491da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
6501da177e4SLinus Torvalds /* iterator */
651def4e38dSTejun Heo static void *disk_seqf_start(struct seq_file *seqf, loff_t *pos)
65268c4d4a7SGreg Kroah-Hartman {
653def4e38dSTejun Heo 	loff_t skip = *pos;
654def4e38dSTejun Heo 	struct class_dev_iter *iter;
655def4e38dSTejun Heo 	struct device *dev;
65668c4d4a7SGreg Kroah-Hartman 
657aeb3d3a8SHarvey Harrison 	iter = kmalloc(sizeof(*iter), GFP_KERNEL);
658def4e38dSTejun Heo 	if (!iter)
659def4e38dSTejun Heo 		return ERR_PTR(-ENOMEM);
660def4e38dSTejun Heo 
661def4e38dSTejun Heo 	seqf->private = iter;
662def4e38dSTejun Heo 	class_dev_iter_init(iter, &block_class, NULL, &disk_type);
663def4e38dSTejun Heo 	do {
664def4e38dSTejun Heo 		dev = class_dev_iter_next(iter);
665def4e38dSTejun Heo 		if (!dev)
666def4e38dSTejun Heo 			return NULL;
667def4e38dSTejun Heo 	} while (skip--);
668def4e38dSTejun Heo 
669def4e38dSTejun Heo 	return dev_to_disk(dev);
67068c4d4a7SGreg Kroah-Hartman }
67168c4d4a7SGreg Kroah-Hartman 
672def4e38dSTejun Heo static void *disk_seqf_next(struct seq_file *seqf, void *v, loff_t *pos)
6731da177e4SLinus Torvalds {
674edfaa7c3SKay Sievers 	struct device *dev;
67566c64afeSGreg Kroah-Hartman 
676def4e38dSTejun Heo 	(*pos)++;
677def4e38dSTejun Heo 	dev = class_dev_iter_next(seqf->private);
6782ac3cee5STejun Heo 	if (dev)
679edfaa7c3SKay Sievers 		return dev_to_disk(dev);
6802ac3cee5STejun Heo 
6811da177e4SLinus Torvalds 	return NULL;
6821da177e4SLinus Torvalds }
6831da177e4SLinus Torvalds 
684def4e38dSTejun Heo static void disk_seqf_stop(struct seq_file *seqf, void *v)
68527f30251SGreg Kroah-Hartman {
686def4e38dSTejun Heo 	struct class_dev_iter *iter = seqf->private;
687def4e38dSTejun Heo 
688def4e38dSTejun Heo 	/* stop is called even after start failed :-( */
689def4e38dSTejun Heo 	if (iter) {
690def4e38dSTejun Heo 		class_dev_iter_exit(iter);
691def4e38dSTejun Heo 		kfree(iter);
692def4e38dSTejun Heo 	}
69327f30251SGreg Kroah-Hartman }
69427f30251SGreg Kroah-Hartman 
695def4e38dSTejun Heo static void *show_partition_start(struct seq_file *seqf, loff_t *pos)
6961da177e4SLinus Torvalds {
697def4e38dSTejun Heo 	static void *p;
6981da177e4SLinus Torvalds 
699def4e38dSTejun Heo 	p = disk_seqf_start(seqf, pos);
700def4e38dSTejun Heo 	if (!IS_ERR(p) && p)
701def4e38dSTejun Heo 		seq_puts(seqf, "major minor  #blocks  name\n\n");
702def4e38dSTejun Heo 	return p;
7031da177e4SLinus Torvalds }
7041da177e4SLinus Torvalds 
705cf771cb5STejun Heo static int show_partition(struct seq_file *seqf, void *v)
7061da177e4SLinus Torvalds {
7071da177e4SLinus Torvalds 	struct gendisk *sgp = v;
708e71bf0d0STejun Heo 	struct disk_part_iter piter;
709e71bf0d0STejun Heo 	struct hd_struct *part;
7101da177e4SLinus Torvalds 	char buf[BDEVNAME_SIZE];
7111da177e4SLinus Torvalds 
7121da177e4SLinus Torvalds 	/* Don't show non-partitionable removeable devices or empty devices */
713b5d0b9dfSTejun Heo 	if (!get_capacity(sgp) || (!disk_partitionable(sgp) &&
714f331c029STejun Heo 				   (sgp->flags & GENHD_FL_REMOVABLE)))
7151da177e4SLinus Torvalds 		return 0;
7161da177e4SLinus Torvalds 	if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO)
7171da177e4SLinus Torvalds 		return 0;
7181da177e4SLinus Torvalds 
7191da177e4SLinus Torvalds 	/* show the full disk and all non-0 size partitions of it */
720074a7acaSTejun Heo 	disk_part_iter_init(&piter, sgp, DISK_PITER_INCL_PART0);
721e71bf0d0STejun Heo 	while ((part = disk_part_iter_next(&piter)))
7221f014290STejun Heo 		seq_printf(seqf, "%4d  %7d %10llu %s\n",
723f331c029STejun Heo 			   MAJOR(part_devt(part)), MINOR(part_devt(part)),
724f331c029STejun Heo 			   (unsigned long long)part->nr_sects >> 1,
725f331c029STejun Heo 			   disk_name(sgp, part->partno, buf));
726e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
7271da177e4SLinus Torvalds 
7281da177e4SLinus Torvalds 	return 0;
7291da177e4SLinus Torvalds }
7301da177e4SLinus Torvalds 
73112f32bb3SJan Engelhardt const struct seq_operations partitions_op = {
732def4e38dSTejun Heo 	.start	= show_partition_start,
733def4e38dSTejun Heo 	.next	= disk_seqf_next,
734def4e38dSTejun Heo 	.stop	= disk_seqf_stop,
7351da177e4SLinus Torvalds 	.show	= show_partition
7361da177e4SLinus Torvalds };
7371da177e4SLinus Torvalds #endif
7381da177e4SLinus Torvalds 
7391da177e4SLinus Torvalds 
740cf771cb5STejun Heo static struct kobject *base_probe(dev_t devt, int *partno, void *data)
7411da177e4SLinus Torvalds {
742edfaa7c3SKay Sievers 	if (request_module("block-major-%d-%d", MAJOR(devt), MINOR(devt)) > 0)
7431da177e4SLinus Torvalds 		/* Make old-style 2.4 aliases work */
744edfaa7c3SKay Sievers 		request_module("block-major-%d", MAJOR(devt));
7451da177e4SLinus Torvalds 	return NULL;
7461da177e4SLinus Torvalds }
7471da177e4SLinus Torvalds 
7481da177e4SLinus Torvalds static int __init genhd_device_init(void)
7491da177e4SLinus Torvalds {
750e105b8bfSDan Williams 	int error;
751e105b8bfSDan Williams 
752e105b8bfSDan Williams 	block_class.dev_kobj = sysfs_dev_block_kobj;
753e105b8bfSDan Williams 	error = class_register(&block_class);
754ee27a558SRoland McGrath 	if (unlikely(error))
755ee27a558SRoland McGrath 		return error;
756edfaa7c3SKay Sievers 	bdev_map = kobj_map_init(base_probe, &block_class_lock);
7571da177e4SLinus Torvalds 	blk_dev_init();
758edfaa7c3SKay Sievers 
759edfaa7c3SKay Sievers #ifndef CONFIG_SYSFS_DEPRECATED
760edfaa7c3SKay Sievers 	/* create top-level block dir */
761edfaa7c3SKay Sievers 	block_depr = kobject_create_and_add("block", NULL);
762edfaa7c3SKay Sievers #endif
763830d3cfbSGreg Kroah-Hartman 	return 0;
7641da177e4SLinus Torvalds }
7651da177e4SLinus Torvalds 
7661da177e4SLinus Torvalds subsys_initcall(genhd_device_init);
7671da177e4SLinus Torvalds 
768edfaa7c3SKay Sievers static ssize_t disk_range_show(struct device *dev,
769edfaa7c3SKay Sievers 			       struct device_attribute *attr, char *buf)
7701da177e4SLinus Torvalds {
771edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
7721da177e4SLinus Torvalds 
773edfaa7c3SKay Sievers 	return sprintf(buf, "%d\n", disk->minors);
7741da177e4SLinus Torvalds }
7751da177e4SLinus Torvalds 
7761f014290STejun Heo static ssize_t disk_ext_range_show(struct device *dev,
7771f014290STejun Heo 				   struct device_attribute *attr, char *buf)
7781f014290STejun Heo {
7791f014290STejun Heo 	struct gendisk *disk = dev_to_disk(dev);
7801f014290STejun Heo 
781b5d0b9dfSTejun Heo 	return sprintf(buf, "%d\n", disk_max_parts(disk));
7821f014290STejun Heo }
7831f014290STejun Heo 
784edfaa7c3SKay Sievers static ssize_t disk_removable_show(struct device *dev,
785edfaa7c3SKay Sievers 				   struct device_attribute *attr, char *buf)
786a7fd6706SKay Sievers {
787edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
788a7fd6706SKay Sievers 
789edfaa7c3SKay Sievers 	return sprintf(buf, "%d\n",
7901da177e4SLinus Torvalds 		       (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0));
791edfaa7c3SKay Sievers }
7921da177e4SLinus Torvalds 
7931c9ce527SKay Sievers static ssize_t disk_ro_show(struct device *dev,
7941c9ce527SKay Sievers 				   struct device_attribute *attr, char *buf)
7951c9ce527SKay Sievers {
7961c9ce527SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
7971c9ce527SKay Sievers 
798b7db9956STejun Heo 	return sprintf(buf, "%d\n", get_disk_ro(disk) ? 1 : 0);
7991c9ce527SKay Sievers }
8001c9ce527SKay Sievers 
801edfaa7c3SKay Sievers static ssize_t disk_capability_show(struct device *dev,
802edfaa7c3SKay Sievers 				    struct device_attribute *attr, char *buf)
80386ce18d7SKristen Carlson Accardi {
804edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
805edfaa7c3SKay Sievers 
806edfaa7c3SKay Sievers 	return sprintf(buf, "%x\n", disk->flags);
80786ce18d7SKristen Carlson Accardi }
808edfaa7c3SKay Sievers 
809edfaa7c3SKay Sievers static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL);
8101f014290STejun Heo static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL);
811edfaa7c3SKay Sievers static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL);
8121c9ce527SKay Sievers static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL);
813e5610521STejun Heo static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL);
814edfaa7c3SKay Sievers static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL);
815074a7acaSTejun Heo static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL);
816c17bb495SAkinobu Mita #ifdef CONFIG_FAIL_MAKE_REQUEST
817edfaa7c3SKay Sievers static struct device_attribute dev_attr_fail =
818eddb2e26STejun Heo 	__ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store);
819c17bb495SAkinobu Mita #endif
820edfaa7c3SKay Sievers 
821edfaa7c3SKay Sievers static struct attribute *disk_attrs[] = {
822edfaa7c3SKay Sievers 	&dev_attr_range.attr,
8231f014290STejun Heo 	&dev_attr_ext_range.attr,
824edfaa7c3SKay Sievers 	&dev_attr_removable.attr,
8251c9ce527SKay Sievers 	&dev_attr_ro.attr,
826edfaa7c3SKay Sievers 	&dev_attr_size.attr,
827edfaa7c3SKay Sievers 	&dev_attr_capability.attr,
828edfaa7c3SKay Sievers 	&dev_attr_stat.attr,
829edfaa7c3SKay Sievers #ifdef CONFIG_FAIL_MAKE_REQUEST
830edfaa7c3SKay Sievers 	&dev_attr_fail.attr,
831edfaa7c3SKay Sievers #endif
832edfaa7c3SKay Sievers 	NULL
8331da177e4SLinus Torvalds };
8341da177e4SLinus Torvalds 
835edfaa7c3SKay Sievers static struct attribute_group disk_attr_group = {
836edfaa7c3SKay Sievers 	.attrs = disk_attrs,
837edfaa7c3SKay Sievers };
838edfaa7c3SKay Sievers 
839edfaa7c3SKay Sievers static struct attribute_group *disk_attr_groups[] = {
840edfaa7c3SKay Sievers 	&disk_attr_group,
841edfaa7c3SKay Sievers 	NULL
842edfaa7c3SKay Sievers };
843edfaa7c3SKay Sievers 
844540eed56STejun Heo static void disk_free_ptbl_rcu_cb(struct rcu_head *head)
845540eed56STejun Heo {
846540eed56STejun Heo 	struct disk_part_tbl *ptbl =
847540eed56STejun Heo 		container_of(head, struct disk_part_tbl, rcu_head);
848540eed56STejun Heo 
849540eed56STejun Heo 	kfree(ptbl);
850540eed56STejun Heo }
851540eed56STejun Heo 
852540eed56STejun Heo /**
853540eed56STejun Heo  * disk_replace_part_tbl - replace disk->part_tbl in RCU-safe way
854540eed56STejun Heo  * @disk: disk to replace part_tbl for
855540eed56STejun Heo  * @new_ptbl: new part_tbl to install
856540eed56STejun Heo  *
857540eed56STejun Heo  * Replace disk->part_tbl with @new_ptbl in RCU-safe way.  The
858540eed56STejun Heo  * original ptbl is freed using RCU callback.
859540eed56STejun Heo  *
860540eed56STejun Heo  * LOCKING:
861540eed56STejun Heo  * Matching bd_mutx locked.
862540eed56STejun Heo  */
863540eed56STejun Heo static void disk_replace_part_tbl(struct gendisk *disk,
864540eed56STejun Heo 				  struct disk_part_tbl *new_ptbl)
865540eed56STejun Heo {
866540eed56STejun Heo 	struct disk_part_tbl *old_ptbl = disk->part_tbl;
867540eed56STejun Heo 
868540eed56STejun Heo 	rcu_assign_pointer(disk->part_tbl, new_ptbl);
869540eed56STejun Heo 	if (old_ptbl)
870540eed56STejun Heo 		call_rcu(&old_ptbl->rcu_head, disk_free_ptbl_rcu_cb);
871540eed56STejun Heo }
872540eed56STejun Heo 
873540eed56STejun Heo /**
874540eed56STejun Heo  * disk_expand_part_tbl - expand disk->part_tbl
875540eed56STejun Heo  * @disk: disk to expand part_tbl for
876540eed56STejun Heo  * @partno: expand such that this partno can fit in
877540eed56STejun Heo  *
878540eed56STejun Heo  * Expand disk->part_tbl such that @partno can fit in.  disk->part_tbl
879540eed56STejun Heo  * uses RCU to allow unlocked dereferencing for stats and other stuff.
880540eed56STejun Heo  *
881540eed56STejun Heo  * LOCKING:
882540eed56STejun Heo  * Matching bd_mutex locked, might sleep.
883540eed56STejun Heo  *
884540eed56STejun Heo  * RETURNS:
885540eed56STejun Heo  * 0 on success, -errno on failure.
886540eed56STejun Heo  */
887540eed56STejun Heo int disk_expand_part_tbl(struct gendisk *disk, int partno)
888540eed56STejun Heo {
889540eed56STejun Heo 	struct disk_part_tbl *old_ptbl = disk->part_tbl;
890540eed56STejun Heo 	struct disk_part_tbl *new_ptbl;
891540eed56STejun Heo 	int len = old_ptbl ? old_ptbl->len : 0;
892540eed56STejun Heo 	int target = partno + 1;
893540eed56STejun Heo 	size_t size;
894540eed56STejun Heo 	int i;
895540eed56STejun Heo 
896540eed56STejun Heo 	/* disk_max_parts() is zero during initialization, ignore if so */
897540eed56STejun Heo 	if (disk_max_parts(disk) && target > disk_max_parts(disk))
898540eed56STejun Heo 		return -EINVAL;
899540eed56STejun Heo 
900540eed56STejun Heo 	if (target <= len)
901540eed56STejun Heo 		return 0;
902540eed56STejun Heo 
903540eed56STejun Heo 	size = sizeof(*new_ptbl) + target * sizeof(new_ptbl->part[0]);
904540eed56STejun Heo 	new_ptbl = kzalloc_node(size, GFP_KERNEL, disk->node_id);
905540eed56STejun Heo 	if (!new_ptbl)
906540eed56STejun Heo 		return -ENOMEM;
907540eed56STejun Heo 
908540eed56STejun Heo 	INIT_RCU_HEAD(&new_ptbl->rcu_head);
909540eed56STejun Heo 	new_ptbl->len = target;
910540eed56STejun Heo 
911540eed56STejun Heo 	for (i = 0; i < len; i++)
912540eed56STejun Heo 		rcu_assign_pointer(new_ptbl->part[i], old_ptbl->part[i]);
913540eed56STejun Heo 
914540eed56STejun Heo 	disk_replace_part_tbl(disk, new_ptbl);
915540eed56STejun Heo 	return 0;
916540eed56STejun Heo }
917540eed56STejun Heo 
918edfaa7c3SKay Sievers static void disk_release(struct device *dev)
9191da177e4SLinus Torvalds {
920edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
921edfaa7c3SKay Sievers 
9221da177e4SLinus Torvalds 	kfree(disk->random);
923540eed56STejun Heo 	disk_replace_part_tbl(disk, NULL);
924074a7acaSTejun Heo 	free_part_stats(&disk->part0);
9251da177e4SLinus Torvalds 	kfree(disk);
9261da177e4SLinus Torvalds }
927edfaa7c3SKay Sievers struct class block_class = {
928edfaa7c3SKay Sievers 	.name		= "block",
9291da177e4SLinus Torvalds };
9301da177e4SLinus Torvalds 
9311826eadfSAdrian Bunk static struct device_type disk_type = {
932edfaa7c3SKay Sievers 	.name		= "disk",
933edfaa7c3SKay Sievers 	.groups		= disk_attr_groups,
934edfaa7c3SKay Sievers 	.release	= disk_release,
9351da177e4SLinus Torvalds };
9361da177e4SLinus Torvalds 
937a6e2ba88SRandy Dunlap #ifdef CONFIG_PROC_FS
938cf771cb5STejun Heo /*
939cf771cb5STejun Heo  * aggregate disk stat collector.  Uses the same stats that the sysfs
940cf771cb5STejun Heo  * entries do, above, but makes them available through one seq_file.
941cf771cb5STejun Heo  *
942cf771cb5STejun Heo  * The output looks suspiciously like /proc/partitions with a bunch of
943cf771cb5STejun Heo  * extra fields.
944cf771cb5STejun Heo  */
945cf771cb5STejun Heo static int diskstats_show(struct seq_file *seqf, void *v)
9461da177e4SLinus Torvalds {
9471da177e4SLinus Torvalds 	struct gendisk *gp = v;
948e71bf0d0STejun Heo 	struct disk_part_iter piter;
949e71bf0d0STejun Heo 	struct hd_struct *hd;
9501da177e4SLinus Torvalds 	char buf[BDEVNAME_SIZE];
951c9959059STejun Heo 	int cpu;
9521da177e4SLinus Torvalds 
9531da177e4SLinus Torvalds 	/*
954ed9e1982STejun Heo 	if (&disk_to_dev(gp)->kobj.entry == block_class.devices.next)
955cf771cb5STejun Heo 		seq_puts(seqf,	"major minor name"
9561da177e4SLinus Torvalds 				"     rio rmerge rsect ruse wio wmerge "
9571da177e4SLinus Torvalds 				"wsect wuse running use aveq"
9581da177e4SLinus Torvalds 				"\n\n");
9591da177e4SLinus Torvalds 	*/
9601da177e4SLinus Torvalds 
961074a7acaSTejun Heo 	disk_part_iter_init(&piter, gp, DISK_PITER_INCL_PART0);
962e71bf0d0STejun Heo 	while ((hd = disk_part_iter_next(&piter))) {
963074a7acaSTejun Heo 		cpu = part_stat_lock();
964c9959059STejun Heo 		part_round_stats(cpu, hd);
965074a7acaSTejun Heo 		part_stat_unlock();
9661f014290STejun Heo 		seq_printf(seqf, "%4d %7d %s %lu %lu %llu "
96728f39d55SJerome Marchand 			   "%u %lu %lu %llu %u %u %u %u\n",
968f331c029STejun Heo 			   MAJOR(part_devt(hd)), MINOR(part_devt(hd)),
969f331c029STejun Heo 			   disk_name(gp, hd->partno, buf),
97028f39d55SJerome Marchand 			   part_stat_read(hd, ios[0]),
97128f39d55SJerome Marchand 			   part_stat_read(hd, merges[0]),
97228f39d55SJerome Marchand 			   (unsigned long long)part_stat_read(hd, sectors[0]),
97328f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, ticks[0])),
97428f39d55SJerome Marchand 			   part_stat_read(hd, ios[1]),
97528f39d55SJerome Marchand 			   part_stat_read(hd, merges[1]),
97628f39d55SJerome Marchand 			   (unsigned long long)part_stat_read(hd, sectors[1]),
97728f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, ticks[1])),
97828f39d55SJerome Marchand 			   hd->in_flight,
97928f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, io_ticks)),
98028f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, time_in_queue))
98128f39d55SJerome Marchand 			);
9821da177e4SLinus Torvalds 	}
983e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
9841da177e4SLinus Torvalds 
9851da177e4SLinus Torvalds 	return 0;
9861da177e4SLinus Torvalds }
9871da177e4SLinus Torvalds 
98812f32bb3SJan Engelhardt const struct seq_operations diskstats_op = {
989def4e38dSTejun Heo 	.start	= disk_seqf_start,
990def4e38dSTejun Heo 	.next	= disk_seqf_next,
991def4e38dSTejun Heo 	.stop	= disk_seqf_stop,
9921da177e4SLinus Torvalds 	.show	= diskstats_show
9931da177e4SLinus Torvalds };
994a6e2ba88SRandy Dunlap #endif /* CONFIG_PROC_FS */
9951da177e4SLinus Torvalds 
9968ce7ad7bSKristen Carlson Accardi static void media_change_notify_thread(struct work_struct *work)
9978ce7ad7bSKristen Carlson Accardi {
9988ce7ad7bSKristen Carlson Accardi 	struct gendisk *gd = container_of(work, struct gendisk, async_notify);
9998ce7ad7bSKristen Carlson Accardi 	char event[] = "MEDIA_CHANGE=1";
10008ce7ad7bSKristen Carlson Accardi 	char *envp[] = { event, NULL };
10018ce7ad7bSKristen Carlson Accardi 
10028ce7ad7bSKristen Carlson Accardi 	/*
10038ce7ad7bSKristen Carlson Accardi 	 * set enviroment vars to indicate which event this is for
10048ce7ad7bSKristen Carlson Accardi 	 * so that user space will know to go check the media status.
10058ce7ad7bSKristen Carlson Accardi 	 */
1006ed9e1982STejun Heo 	kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp);
10078ce7ad7bSKristen Carlson Accardi 	put_device(gd->driverfs_dev);
10088ce7ad7bSKristen Carlson Accardi }
10098ce7ad7bSKristen Carlson Accardi 
10101826eadfSAdrian Bunk #if 0
10118ce7ad7bSKristen Carlson Accardi void genhd_media_change_notify(struct gendisk *disk)
10128ce7ad7bSKristen Carlson Accardi {
10138ce7ad7bSKristen Carlson Accardi 	get_device(disk->driverfs_dev);
10148ce7ad7bSKristen Carlson Accardi 	schedule_work(&disk->async_notify);
10158ce7ad7bSKristen Carlson Accardi }
10168ce7ad7bSKristen Carlson Accardi EXPORT_SYMBOL_GPL(genhd_media_change_notify);
10171826eadfSAdrian Bunk #endif  /*  0  */
10188ce7ad7bSKristen Carlson Accardi 
1019cf771cb5STejun Heo dev_t blk_lookup_devt(const char *name, int partno)
1020edfaa7c3SKay Sievers {
1021edfaa7c3SKay Sievers 	dev_t devt = MKDEV(0, 0);
1022def4e38dSTejun Heo 	struct class_dev_iter iter;
1023def4e38dSTejun Heo 	struct device *dev;
1024edfaa7c3SKay Sievers 
1025def4e38dSTejun Heo 	class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
1026def4e38dSTejun Heo 	while ((dev = class_dev_iter_next(&iter))) {
1027def4e38dSTejun Heo 		struct gendisk *disk = dev_to_disk(dev);
1028548b10ebSTejun Heo 		struct hd_struct *part;
1029def4e38dSTejun Heo 
1030f331c029STejun Heo 		if (strcmp(dev->bus_id, name))
1031f331c029STejun Heo 			continue;
1032f331c029STejun Heo 
1033e71bf0d0STejun Heo 		part = disk_get_part(disk, partno);
1034*2bbedcb4STejun Heo 		if (part) {
1035f331c029STejun Heo 			devt = part_devt(part);
1036e71bf0d0STejun Heo 			disk_put_part(part);
1037f331c029STejun Heo 			break;
1038def4e38dSTejun Heo 		}
1039548b10ebSTejun Heo 		disk_put_part(part);
1040548b10ebSTejun Heo 	}
1041def4e38dSTejun Heo 	class_dev_iter_exit(&iter);
1042edfaa7c3SKay Sievers 	return devt;
1043edfaa7c3SKay Sievers }
1044edfaa7c3SKay Sievers EXPORT_SYMBOL(blk_lookup_devt);
1045edfaa7c3SKay Sievers 
10461da177e4SLinus Torvalds struct gendisk *alloc_disk(int minors)
10471da177e4SLinus Torvalds {
10481946089aSChristoph Lameter 	return alloc_disk_node(minors, -1);
10491946089aSChristoph Lameter }
1050689d6facSTejun Heo EXPORT_SYMBOL(alloc_disk);
10511946089aSChristoph Lameter 
10521946089aSChristoph Lameter struct gendisk *alloc_disk_node(int minors, int node_id)
10531946089aSChristoph Lameter {
10541946089aSChristoph Lameter 	struct gendisk *disk;
10551946089aSChristoph Lameter 
105694f6030cSChristoph Lameter 	disk = kmalloc_node(sizeof(struct gendisk),
105794f6030cSChristoph Lameter 				GFP_KERNEL | __GFP_ZERO, node_id);
10581da177e4SLinus Torvalds 	if (disk) {
1059074a7acaSTejun Heo 		if (!init_part_stats(&disk->part0)) {
10601da177e4SLinus Torvalds 			kfree(disk);
10611da177e4SLinus Torvalds 			return NULL;
10621da177e4SLinus Torvalds 		}
1063540eed56STejun Heo 		if (disk_expand_part_tbl(disk, 0)) {
1064074a7acaSTejun Heo 			free_part_stats(&disk->part0);
10651da177e4SLinus Torvalds 			kfree(disk);
10661da177e4SLinus Torvalds 			return NULL;
10671da177e4SLinus Torvalds 		}
1068540eed56STejun Heo 		disk->part_tbl->part[0] = &disk->part0;
1069b5d0b9dfSTejun Heo 
10701da177e4SLinus Torvalds 		disk->minors = minors;
10711da177e4SLinus Torvalds 		rand_initialize_disk(disk);
1072ed9e1982STejun Heo 		disk_to_dev(disk)->class = &block_class;
1073ed9e1982STejun Heo 		disk_to_dev(disk)->type = &disk_type;
1074ed9e1982STejun Heo 		device_initialize(disk_to_dev(disk));
10758ce7ad7bSKristen Carlson Accardi 		INIT_WORK(&disk->async_notify,
10768ce7ad7bSKristen Carlson Accardi 			media_change_notify_thread);
1077540eed56STejun Heo 		disk->node_id = node_id;
10781da177e4SLinus Torvalds 	}
10791da177e4SLinus Torvalds 	return disk;
10801da177e4SLinus Torvalds }
10811946089aSChristoph Lameter EXPORT_SYMBOL(alloc_disk_node);
10821da177e4SLinus Torvalds 
10831da177e4SLinus Torvalds struct kobject *get_disk(struct gendisk *disk)
10841da177e4SLinus Torvalds {
10851da177e4SLinus Torvalds 	struct module *owner;
10861da177e4SLinus Torvalds 	struct kobject *kobj;
10871da177e4SLinus Torvalds 
10881da177e4SLinus Torvalds 	if (!disk->fops)
10891da177e4SLinus Torvalds 		return NULL;
10901da177e4SLinus Torvalds 	owner = disk->fops->owner;
10911da177e4SLinus Torvalds 	if (owner && !try_module_get(owner))
10921da177e4SLinus Torvalds 		return NULL;
1093ed9e1982STejun Heo 	kobj = kobject_get(&disk_to_dev(disk)->kobj);
10941da177e4SLinus Torvalds 	if (kobj == NULL) {
10951da177e4SLinus Torvalds 		module_put(owner);
10961da177e4SLinus Torvalds 		return NULL;
10971da177e4SLinus Torvalds 	}
10981da177e4SLinus Torvalds 	return kobj;
10991da177e4SLinus Torvalds 
11001da177e4SLinus Torvalds }
11011da177e4SLinus Torvalds 
11021da177e4SLinus Torvalds EXPORT_SYMBOL(get_disk);
11031da177e4SLinus Torvalds 
11041da177e4SLinus Torvalds void put_disk(struct gendisk *disk)
11051da177e4SLinus Torvalds {
11061da177e4SLinus Torvalds 	if (disk)
1107ed9e1982STejun Heo 		kobject_put(&disk_to_dev(disk)->kobj);
11081da177e4SLinus Torvalds }
11091da177e4SLinus Torvalds 
11101da177e4SLinus Torvalds EXPORT_SYMBOL(put_disk);
11111da177e4SLinus Torvalds 
11121da177e4SLinus Torvalds void set_device_ro(struct block_device *bdev, int flag)
11131da177e4SLinus Torvalds {
11141da177e4SLinus Torvalds 	bdev->bd_part->policy = flag;
11151da177e4SLinus Torvalds }
11161da177e4SLinus Torvalds 
11171da177e4SLinus Torvalds EXPORT_SYMBOL(set_device_ro);
11181da177e4SLinus Torvalds 
11191da177e4SLinus Torvalds void set_disk_ro(struct gendisk *disk, int flag)
11201da177e4SLinus Torvalds {
1121e71bf0d0STejun Heo 	struct disk_part_iter piter;
1122e71bf0d0STejun Heo 	struct hd_struct *part;
1123e71bf0d0STejun Heo 
1124b7db9956STejun Heo 	disk_part_iter_init(&piter, disk,
1125b7db9956STejun Heo 			    DISK_PITER_INCL_EMPTY | DISK_PITER_INCL_PART0);
1126e71bf0d0STejun Heo 	while ((part = disk_part_iter_next(&piter)))
1127e71bf0d0STejun Heo 		part->policy = flag;
1128e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
11291da177e4SLinus Torvalds }
11301da177e4SLinus Torvalds 
11311da177e4SLinus Torvalds EXPORT_SYMBOL(set_disk_ro);
11321da177e4SLinus Torvalds 
11331da177e4SLinus Torvalds int bdev_read_only(struct block_device *bdev)
11341da177e4SLinus Torvalds {
11351da177e4SLinus Torvalds 	if (!bdev)
11361da177e4SLinus Torvalds 		return 0;
11371da177e4SLinus Torvalds 	return bdev->bd_part->policy;
11381da177e4SLinus Torvalds }
11391da177e4SLinus Torvalds 
11401da177e4SLinus Torvalds EXPORT_SYMBOL(bdev_read_only);
11411da177e4SLinus Torvalds 
1142cf771cb5STejun Heo int invalidate_partition(struct gendisk *disk, int partno)
11431da177e4SLinus Torvalds {
11441da177e4SLinus Torvalds 	int res = 0;
1145cf771cb5STejun Heo 	struct block_device *bdev = bdget_disk(disk, partno);
11461da177e4SLinus Torvalds 	if (bdev) {
11472ef41634SChristoph Hellwig 		fsync_bdev(bdev);
11482ef41634SChristoph Hellwig 		res = __invalidate_device(bdev);
11491da177e4SLinus Torvalds 		bdput(bdev);
11501da177e4SLinus Torvalds 	}
11511da177e4SLinus Torvalds 	return res;
11521da177e4SLinus Torvalds }
11531da177e4SLinus Torvalds 
11541da177e4SLinus Torvalds EXPORT_SYMBOL(invalidate_partition);
1155