xref: /linux/block/genhd.c (revision 496aa8a98f5ab22ced46be5dc2087cdf3d029bd7)
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  * @devt: out parameter for resulting dev_t
362bcce3de1STejun Heo  *
363bcce3de1STejun Heo  * Allocate a dev_t for block device.
364bcce3de1STejun Heo  *
365bcce3de1STejun Heo  * RETURNS:
366bcce3de1STejun Heo  * 0 on success, allocated dev_t is returned in *@devt.  -errno on
367bcce3de1STejun Heo  * failure.
368bcce3de1STejun Heo  *
369bcce3de1STejun Heo  * CONTEXT:
370bcce3de1STejun Heo  * Might sleep.
371bcce3de1STejun Heo  */
372bcce3de1STejun Heo int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
373bcce3de1STejun Heo {
374bcce3de1STejun Heo 	struct gendisk *disk = part_to_disk(part);
375bcce3de1STejun Heo 	int idx, rc;
376bcce3de1STejun Heo 
377bcce3de1STejun Heo 	/* in consecutive minor range? */
378bcce3de1STejun Heo 	if (part->partno < disk->minors) {
379bcce3de1STejun Heo 		*devt = MKDEV(disk->major, disk->first_minor + part->partno);
380bcce3de1STejun Heo 		return 0;
381bcce3de1STejun Heo 	}
382bcce3de1STejun Heo 
383bcce3de1STejun Heo 	/* allocate ext devt */
384bcce3de1STejun Heo 	do {
385bcce3de1STejun Heo 		if (!idr_pre_get(&ext_devt_idr, GFP_KERNEL))
386bcce3de1STejun Heo 			return -ENOMEM;
387bcce3de1STejun Heo 		rc = idr_get_new(&ext_devt_idr, part, &idx);
388bcce3de1STejun Heo 	} while (rc == -EAGAIN);
389bcce3de1STejun Heo 
390bcce3de1STejun Heo 	if (rc)
391bcce3de1STejun Heo 		return rc;
392bcce3de1STejun Heo 
393bcce3de1STejun Heo 	if (idx > MAX_EXT_DEVT) {
394bcce3de1STejun Heo 		idr_remove(&ext_devt_idr, idx);
395bcce3de1STejun Heo 		return -EBUSY;
396bcce3de1STejun Heo 	}
397bcce3de1STejun Heo 
398870d6656STejun Heo 	*devt = MKDEV(BLOCK_EXT_MAJOR, blk_mangle_minor(idx));
399bcce3de1STejun Heo 	return 0;
400bcce3de1STejun Heo }
401bcce3de1STejun Heo 
402bcce3de1STejun Heo /**
403bcce3de1STejun Heo  * blk_free_devt - free a dev_t
404bcce3de1STejun Heo  * @devt: dev_t to free
405bcce3de1STejun Heo  *
406bcce3de1STejun Heo  * Free @devt which was allocated using blk_alloc_devt().
407bcce3de1STejun Heo  *
408bcce3de1STejun Heo  * CONTEXT:
409bcce3de1STejun Heo  * Might sleep.
410bcce3de1STejun Heo  */
411bcce3de1STejun Heo void blk_free_devt(dev_t devt)
412bcce3de1STejun Heo {
413bcce3de1STejun Heo 	might_sleep();
414bcce3de1STejun Heo 
415bcce3de1STejun Heo 	if (devt == MKDEV(0, 0))
416bcce3de1STejun Heo 		return;
417bcce3de1STejun Heo 
418bcce3de1STejun Heo 	if (MAJOR(devt) == BLOCK_EXT_MAJOR) {
419bcce3de1STejun Heo 		mutex_lock(&ext_devt_mutex);
420870d6656STejun Heo 		idr_remove(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
421bcce3de1STejun Heo 		mutex_unlock(&ext_devt_mutex);
422bcce3de1STejun Heo 	}
423bcce3de1STejun Heo }
424bcce3de1STejun Heo 
4251f014290STejun Heo static char *bdevt_str(dev_t devt, char *buf)
4261f014290STejun Heo {
4271f014290STejun Heo 	if (MAJOR(devt) <= 0xff && MINOR(devt) <= 0xff) {
4281f014290STejun Heo 		char tbuf[BDEVT_SIZE];
4291f014290STejun Heo 		snprintf(tbuf, BDEVT_SIZE, "%02x%02x", MAJOR(devt), MINOR(devt));
4301f014290STejun Heo 		snprintf(buf, BDEVT_SIZE, "%-9s", tbuf);
4311f014290STejun Heo 	} else
4321f014290STejun Heo 		snprintf(buf, BDEVT_SIZE, "%03x:%05x", MAJOR(devt), MINOR(devt));
4331f014290STejun Heo 
4341f014290STejun Heo 	return buf;
4351f014290STejun Heo }
4361f014290STejun Heo 
4371da177e4SLinus Torvalds /*
4381da177e4SLinus Torvalds  * Register device numbers dev..(dev+range-1)
4391da177e4SLinus Torvalds  * range must be nonzero
4401da177e4SLinus Torvalds  * The hash chain is sorted on range, so that subranges can override.
4411da177e4SLinus Torvalds  */
442edfaa7c3SKay Sievers void blk_register_region(dev_t devt, unsigned long range, struct module *module,
4431da177e4SLinus Torvalds 			 struct kobject *(*probe)(dev_t, int *, void *),
4441da177e4SLinus Torvalds 			 int (*lock)(dev_t, void *), void *data)
4451da177e4SLinus Torvalds {
446edfaa7c3SKay Sievers 	kobj_map(bdev_map, devt, range, module, probe, lock, data);
4471da177e4SLinus Torvalds }
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds EXPORT_SYMBOL(blk_register_region);
4501da177e4SLinus Torvalds 
451edfaa7c3SKay Sievers void blk_unregister_region(dev_t devt, unsigned long range)
4521da177e4SLinus Torvalds {
453edfaa7c3SKay Sievers 	kobj_unmap(bdev_map, devt, range);
4541da177e4SLinus Torvalds }
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds EXPORT_SYMBOL(blk_unregister_region);
4571da177e4SLinus Torvalds 
458cf771cb5STejun Heo static struct kobject *exact_match(dev_t devt, int *partno, void *data)
4591da177e4SLinus Torvalds {
4601da177e4SLinus Torvalds 	struct gendisk *p = data;
461edfaa7c3SKay Sievers 
462ed9e1982STejun Heo 	return &disk_to_dev(p)->kobj;
4631da177e4SLinus Torvalds }
4641da177e4SLinus Torvalds 
465edfaa7c3SKay Sievers static int exact_lock(dev_t devt, void *data)
4661da177e4SLinus Torvalds {
4671da177e4SLinus Torvalds 	struct gendisk *p = data;
4681da177e4SLinus Torvalds 
4691da177e4SLinus Torvalds 	if (!get_disk(p))
4701da177e4SLinus Torvalds 		return -1;
4711da177e4SLinus Torvalds 	return 0;
4721da177e4SLinus Torvalds }
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds /**
4751da177e4SLinus Torvalds  * add_disk - add partitioning information to kernel list
4761da177e4SLinus Torvalds  * @disk: per-device partitioning information
4771da177e4SLinus Torvalds  *
4781da177e4SLinus Torvalds  * This function registers the partitioning information in @disk
4791da177e4SLinus Torvalds  * with the kernel.
4803e1a7ff8STejun Heo  *
4813e1a7ff8STejun Heo  * FIXME: error handling
4821da177e4SLinus Torvalds  */
4831da177e4SLinus Torvalds void add_disk(struct gendisk *disk)
4841da177e4SLinus Torvalds {
485cf0ca9feSPeter Zijlstra 	struct backing_dev_info *bdi;
4863e1a7ff8STejun Heo 	dev_t devt;
4876ffeea77SGreg Kroah-Hartman 	int retval;
488cf0ca9feSPeter Zijlstra 
4893e1a7ff8STejun Heo 	/* minors == 0 indicates to use ext devt from part0 and should
4903e1a7ff8STejun Heo 	 * be accompanied with EXT_DEVT flag.  Make sure all
4913e1a7ff8STejun Heo 	 * parameters make sense.
4923e1a7ff8STejun Heo 	 */
4933e1a7ff8STejun Heo 	WARN_ON(disk->minors && !(disk->major || disk->first_minor));
4943e1a7ff8STejun Heo 	WARN_ON(!disk->minors && !(disk->flags & GENHD_FL_EXT_DEVT));
4953e1a7ff8STejun Heo 
4961da177e4SLinus Torvalds 	disk->flags |= GENHD_FL_UP;
4973e1a7ff8STejun Heo 
4983e1a7ff8STejun Heo 	retval = blk_alloc_devt(&disk->part0, &devt);
4993e1a7ff8STejun Heo 	if (retval) {
5003e1a7ff8STejun Heo 		WARN_ON(1);
5013e1a7ff8STejun Heo 		return;
5023e1a7ff8STejun Heo 	}
5033e1a7ff8STejun Heo 	disk_to_dev(disk)->devt = devt;
5043e1a7ff8STejun Heo 
5053e1a7ff8STejun Heo 	/* ->major and ->first_minor aren't supposed to be
5063e1a7ff8STejun Heo 	 * dereferenced from here on, but set them just in case.
5073e1a7ff8STejun Heo 	 */
5083e1a7ff8STejun Heo 	disk->major = MAJOR(devt);
5093e1a7ff8STejun Heo 	disk->first_minor = MINOR(devt);
5103e1a7ff8STejun Heo 
511f331c029STejun Heo 	blk_register_region(disk_devt(disk), disk->minors, NULL,
512f331c029STejun Heo 			    exact_match, exact_lock, disk);
5131da177e4SLinus Torvalds 	register_disk(disk);
5141da177e4SLinus Torvalds 	blk_register_queue(disk);
515cf0ca9feSPeter Zijlstra 
516cf0ca9feSPeter Zijlstra 	bdi = &disk->queue->backing_dev_info;
517f331c029STejun Heo 	bdi_register_dev(bdi, disk_devt(disk));
518ed9e1982STejun Heo 	retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj,
519ed9e1982STejun Heo 				   "bdi");
5206ffeea77SGreg Kroah-Hartman 	WARN_ON(retval);
5211da177e4SLinus Torvalds }
5221da177e4SLinus Torvalds 
5231da177e4SLinus Torvalds EXPORT_SYMBOL(add_disk);
5241da177e4SLinus Torvalds EXPORT_SYMBOL(del_gendisk);	/* in partitions/check.c */
5251da177e4SLinus Torvalds 
5261da177e4SLinus Torvalds void unlink_gendisk(struct gendisk *disk)
5271da177e4SLinus Torvalds {
528ed9e1982STejun Heo 	sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi");
529cf0ca9feSPeter Zijlstra 	bdi_unregister(&disk->queue->backing_dev_info);
5301da177e4SLinus Torvalds 	blk_unregister_queue(disk);
531f331c029STejun Heo 	blk_unregister_region(disk_devt(disk), disk->minors);
5321da177e4SLinus Torvalds }
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds /**
5351da177e4SLinus Torvalds  * get_gendisk - get partitioning information for a given device
536710027a4SRandy Dunlap  * @devt: device to get partitioning information for
537*496aa8a9SRandy Dunlap  * @partno: returned partition index
5381da177e4SLinus Torvalds  *
5391da177e4SLinus Torvalds  * This function gets the structure containing partitioning
540710027a4SRandy Dunlap  * information for the given device @devt.
5411da177e4SLinus Torvalds  */
542cf771cb5STejun Heo struct gendisk *get_gendisk(dev_t devt, int *partno)
5431da177e4SLinus Torvalds {
544bcce3de1STejun Heo 	struct gendisk *disk = NULL;
545edfaa7c3SKay Sievers 
546bcce3de1STejun Heo 	if (MAJOR(devt) != BLOCK_EXT_MAJOR) {
547bcce3de1STejun Heo 		struct kobject *kobj;
548bcce3de1STejun Heo 
549bcce3de1STejun Heo 		kobj = kobj_lookup(bdev_map, devt, partno);
550bcce3de1STejun Heo 		if (kobj)
551bcce3de1STejun Heo 			disk = dev_to_disk(kobj_to_dev(kobj));
552bcce3de1STejun Heo 	} else {
553bcce3de1STejun Heo 		struct hd_struct *part;
554bcce3de1STejun Heo 
555bcce3de1STejun Heo 		mutex_lock(&ext_devt_mutex);
556870d6656STejun Heo 		part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
557bcce3de1STejun Heo 		if (part && get_disk(part_to_disk(part))) {
558bcce3de1STejun Heo 			*partno = part->partno;
559bcce3de1STejun Heo 			disk = part_to_disk(part);
560bcce3de1STejun Heo 		}
561bcce3de1STejun Heo 		mutex_unlock(&ext_devt_mutex);
562bcce3de1STejun Heo 	}
563bcce3de1STejun Heo 
564bcce3de1STejun Heo 	return disk;
5651da177e4SLinus Torvalds }
5661da177e4SLinus Torvalds 
567f331c029STejun Heo /**
568f331c029STejun Heo  * bdget_disk - do bdget() by gendisk and partition number
569f331c029STejun Heo  * @disk: gendisk of interest
570f331c029STejun Heo  * @partno: partition number
571f331c029STejun Heo  *
572f331c029STejun Heo  * Find partition @partno from @disk, do bdget() on it.
573f331c029STejun Heo  *
574f331c029STejun Heo  * CONTEXT:
575f331c029STejun Heo  * Don't care.
576f331c029STejun Heo  *
577f331c029STejun Heo  * RETURNS:
578f331c029STejun Heo  * Resulting block_device on success, NULL on failure.
579f331c029STejun Heo  */
580aeb3d3a8SHarvey Harrison struct block_device *bdget_disk(struct gendisk *disk, int partno)
581f331c029STejun Heo {
582e71bf0d0STejun Heo 	struct hd_struct *part;
583548b10ebSTejun Heo 	struct block_device *bdev = NULL;
584f331c029STejun Heo 
585e71bf0d0STejun Heo 	part = disk_get_part(disk, partno);
5862bbedcb4STejun Heo 	if (part)
587548b10ebSTejun Heo 		bdev = bdget(part_devt(part));
588e71bf0d0STejun Heo 	disk_put_part(part);
589f331c029STejun Heo 
590548b10ebSTejun Heo 	return bdev;
591f331c029STejun Heo }
592f331c029STejun Heo EXPORT_SYMBOL(bdget_disk);
593f331c029STejun Heo 
594dd2a345fSDave Gilbert /*
5955c6f35c5SGreg Kroah-Hartman  * print a full list of all partitions - intended for places where the root
5965c6f35c5SGreg Kroah-Hartman  * filesystem can't be mounted and thus to give the victim some idea of what
5975c6f35c5SGreg Kroah-Hartman  * went wrong
5985c6f35c5SGreg Kroah-Hartman  */
5995c6f35c5SGreg Kroah-Hartman void __init printk_all_partitions(void)
6005c6f35c5SGreg Kroah-Hartman {
601def4e38dSTejun Heo 	struct class_dev_iter iter;
602def4e38dSTejun Heo 	struct device *dev;
603def4e38dSTejun Heo 
604def4e38dSTejun Heo 	class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
605def4e38dSTejun Heo 	while ((dev = class_dev_iter_next(&iter))) {
606def4e38dSTejun Heo 		struct gendisk *disk = dev_to_disk(dev);
607e71bf0d0STejun Heo 		struct disk_part_iter piter;
608e71bf0d0STejun Heo 		struct hd_struct *part;
6091f014290STejun Heo 		char name_buf[BDEVNAME_SIZE];
6101f014290STejun Heo 		char devt_buf[BDEVT_SIZE];
611def4e38dSTejun Heo 
612def4e38dSTejun Heo 		/*
613def4e38dSTejun Heo 		 * Don't show empty devices or things that have been
614def4e38dSTejun Heo 		 * surpressed
615def4e38dSTejun Heo 		 */
616def4e38dSTejun Heo 		if (get_capacity(disk) == 0 ||
617def4e38dSTejun Heo 		    (disk->flags & GENHD_FL_SUPPRESS_PARTITION_INFO))
618def4e38dSTejun Heo 			continue;
619def4e38dSTejun Heo 
620def4e38dSTejun Heo 		/*
621def4e38dSTejun Heo 		 * Note, unlike /proc/partitions, I am showing the
622def4e38dSTejun Heo 		 * numbers in hex - the same format as the root=
623def4e38dSTejun Heo 		 * option takes.
624def4e38dSTejun Heo 		 */
625074a7acaSTejun Heo 		disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0);
626074a7acaSTejun Heo 		while ((part = disk_part_iter_next(&piter))) {
627074a7acaSTejun Heo 			bool is_part0 = part == &disk->part0;
628074a7acaSTejun Heo 
629074a7acaSTejun Heo 			printk("%s%s %10llu %s", is_part0 ? "" : "  ",
630074a7acaSTejun Heo 			       bdevt_str(part_devt(part), devt_buf),
631074a7acaSTejun Heo 			       (unsigned long long)part->nr_sects >> 1,
632074a7acaSTejun Heo 			       disk_name(disk, part->partno, name_buf));
633074a7acaSTejun Heo 			if (is_part0) {
634def4e38dSTejun Heo 				if (disk->driverfs_dev != NULL &&
635def4e38dSTejun Heo 				    disk->driverfs_dev->driver != NULL)
636def4e38dSTejun Heo 					printk(" driver: %s\n",
637def4e38dSTejun Heo 					      disk->driverfs_dev->driver->name);
638def4e38dSTejun Heo 				else
639def4e38dSTejun Heo 					printk(" (driver?)\n");
640074a7acaSTejun Heo 			} else
641074a7acaSTejun Heo 				printk("\n");
642074a7acaSTejun Heo 		}
643e71bf0d0STejun Heo 		disk_part_iter_exit(&piter);
644def4e38dSTejun Heo 	}
645def4e38dSTejun Heo 	class_dev_iter_exit(&iter);
646dd2a345fSDave Gilbert }
647dd2a345fSDave Gilbert 
6481da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
6491da177e4SLinus Torvalds /* iterator */
650def4e38dSTejun Heo static void *disk_seqf_start(struct seq_file *seqf, loff_t *pos)
65168c4d4a7SGreg Kroah-Hartman {
652def4e38dSTejun Heo 	loff_t skip = *pos;
653def4e38dSTejun Heo 	struct class_dev_iter *iter;
654def4e38dSTejun Heo 	struct device *dev;
65568c4d4a7SGreg Kroah-Hartman 
656aeb3d3a8SHarvey Harrison 	iter = kmalloc(sizeof(*iter), GFP_KERNEL);
657def4e38dSTejun Heo 	if (!iter)
658def4e38dSTejun Heo 		return ERR_PTR(-ENOMEM);
659def4e38dSTejun Heo 
660def4e38dSTejun Heo 	seqf->private = iter;
661def4e38dSTejun Heo 	class_dev_iter_init(iter, &block_class, NULL, &disk_type);
662def4e38dSTejun Heo 	do {
663def4e38dSTejun Heo 		dev = class_dev_iter_next(iter);
664def4e38dSTejun Heo 		if (!dev)
665def4e38dSTejun Heo 			return NULL;
666def4e38dSTejun Heo 	} while (skip--);
667def4e38dSTejun Heo 
668def4e38dSTejun Heo 	return dev_to_disk(dev);
66968c4d4a7SGreg Kroah-Hartman }
67068c4d4a7SGreg Kroah-Hartman 
671def4e38dSTejun Heo static void *disk_seqf_next(struct seq_file *seqf, void *v, loff_t *pos)
6721da177e4SLinus Torvalds {
673edfaa7c3SKay Sievers 	struct device *dev;
67466c64afeSGreg Kroah-Hartman 
675def4e38dSTejun Heo 	(*pos)++;
676def4e38dSTejun Heo 	dev = class_dev_iter_next(seqf->private);
6772ac3cee5STejun Heo 	if (dev)
678edfaa7c3SKay Sievers 		return dev_to_disk(dev);
6792ac3cee5STejun Heo 
6801da177e4SLinus Torvalds 	return NULL;
6811da177e4SLinus Torvalds }
6821da177e4SLinus Torvalds 
683def4e38dSTejun Heo static void disk_seqf_stop(struct seq_file *seqf, void *v)
68427f30251SGreg Kroah-Hartman {
685def4e38dSTejun Heo 	struct class_dev_iter *iter = seqf->private;
686def4e38dSTejun Heo 
687def4e38dSTejun Heo 	/* stop is called even after start failed :-( */
688def4e38dSTejun Heo 	if (iter) {
689def4e38dSTejun Heo 		class_dev_iter_exit(iter);
690def4e38dSTejun Heo 		kfree(iter);
691def4e38dSTejun Heo 	}
69227f30251SGreg Kroah-Hartman }
69327f30251SGreg Kroah-Hartman 
694def4e38dSTejun Heo static void *show_partition_start(struct seq_file *seqf, loff_t *pos)
6951da177e4SLinus Torvalds {
696def4e38dSTejun Heo 	static void *p;
6971da177e4SLinus Torvalds 
698def4e38dSTejun Heo 	p = disk_seqf_start(seqf, pos);
699243294daSTejun Heo 	if (!IS_ERR(p) && p && !*pos)
700def4e38dSTejun Heo 		seq_puts(seqf, "major minor  #blocks  name\n\n");
701def4e38dSTejun Heo 	return p;
7021da177e4SLinus Torvalds }
7031da177e4SLinus Torvalds 
704cf771cb5STejun Heo static int show_partition(struct seq_file *seqf, void *v)
7051da177e4SLinus Torvalds {
7061da177e4SLinus Torvalds 	struct gendisk *sgp = v;
707e71bf0d0STejun Heo 	struct disk_part_iter piter;
708e71bf0d0STejun Heo 	struct hd_struct *part;
7091da177e4SLinus Torvalds 	char buf[BDEVNAME_SIZE];
7101da177e4SLinus Torvalds 
7111da177e4SLinus Torvalds 	/* Don't show non-partitionable removeable devices or empty devices */
712b5d0b9dfSTejun Heo 	if (!get_capacity(sgp) || (!disk_partitionable(sgp) &&
713f331c029STejun Heo 				   (sgp->flags & GENHD_FL_REMOVABLE)))
7141da177e4SLinus Torvalds 		return 0;
7151da177e4SLinus Torvalds 	if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO)
7161da177e4SLinus Torvalds 		return 0;
7171da177e4SLinus Torvalds 
7181da177e4SLinus Torvalds 	/* show the full disk and all non-0 size partitions of it */
719074a7acaSTejun Heo 	disk_part_iter_init(&piter, sgp, DISK_PITER_INCL_PART0);
720e71bf0d0STejun Heo 	while ((part = disk_part_iter_next(&piter)))
7211f014290STejun Heo 		seq_printf(seqf, "%4d  %7d %10llu %s\n",
722f331c029STejun Heo 			   MAJOR(part_devt(part)), MINOR(part_devt(part)),
723f331c029STejun Heo 			   (unsigned long long)part->nr_sects >> 1,
724f331c029STejun Heo 			   disk_name(sgp, part->partno, buf));
725e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
7261da177e4SLinus Torvalds 
7271da177e4SLinus Torvalds 	return 0;
7281da177e4SLinus Torvalds }
7291da177e4SLinus Torvalds 
73012f32bb3SJan Engelhardt const struct seq_operations partitions_op = {
731def4e38dSTejun Heo 	.start	= show_partition_start,
732def4e38dSTejun Heo 	.next	= disk_seqf_next,
733def4e38dSTejun Heo 	.stop	= disk_seqf_stop,
7341da177e4SLinus Torvalds 	.show	= show_partition
7351da177e4SLinus Torvalds };
7361da177e4SLinus Torvalds #endif
7371da177e4SLinus Torvalds 
7381da177e4SLinus Torvalds 
739cf771cb5STejun Heo static struct kobject *base_probe(dev_t devt, int *partno, void *data)
7401da177e4SLinus Torvalds {
741edfaa7c3SKay Sievers 	if (request_module("block-major-%d-%d", MAJOR(devt), MINOR(devt)) > 0)
7421da177e4SLinus Torvalds 		/* Make old-style 2.4 aliases work */
743edfaa7c3SKay Sievers 		request_module("block-major-%d", MAJOR(devt));
7441da177e4SLinus Torvalds 	return NULL;
7451da177e4SLinus Torvalds }
7461da177e4SLinus Torvalds 
7471da177e4SLinus Torvalds static int __init genhd_device_init(void)
7481da177e4SLinus Torvalds {
749e105b8bfSDan Williams 	int error;
750e105b8bfSDan Williams 
751e105b8bfSDan Williams 	block_class.dev_kobj = sysfs_dev_block_kobj;
752e105b8bfSDan Williams 	error = class_register(&block_class);
753ee27a558SRoland McGrath 	if (unlikely(error))
754ee27a558SRoland McGrath 		return error;
755edfaa7c3SKay Sievers 	bdev_map = kobj_map_init(base_probe, &block_class_lock);
7561da177e4SLinus Torvalds 	blk_dev_init();
757edfaa7c3SKay Sievers 
758edfaa7c3SKay Sievers #ifndef CONFIG_SYSFS_DEPRECATED
759edfaa7c3SKay Sievers 	/* create top-level block dir */
760edfaa7c3SKay Sievers 	block_depr = kobject_create_and_add("block", NULL);
761edfaa7c3SKay Sievers #endif
762830d3cfbSGreg Kroah-Hartman 	return 0;
7631da177e4SLinus Torvalds }
7641da177e4SLinus Torvalds 
7651da177e4SLinus Torvalds subsys_initcall(genhd_device_init);
7661da177e4SLinus Torvalds 
767edfaa7c3SKay Sievers static ssize_t disk_range_show(struct device *dev,
768edfaa7c3SKay Sievers 			       struct device_attribute *attr, char *buf)
7691da177e4SLinus Torvalds {
770edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
7711da177e4SLinus Torvalds 
772edfaa7c3SKay Sievers 	return sprintf(buf, "%d\n", disk->minors);
7731da177e4SLinus Torvalds }
7741da177e4SLinus Torvalds 
7751f014290STejun Heo static ssize_t disk_ext_range_show(struct device *dev,
7761f014290STejun Heo 				   struct device_attribute *attr, char *buf)
7771f014290STejun Heo {
7781f014290STejun Heo 	struct gendisk *disk = dev_to_disk(dev);
7791f014290STejun Heo 
780b5d0b9dfSTejun Heo 	return sprintf(buf, "%d\n", disk_max_parts(disk));
7811f014290STejun Heo }
7821f014290STejun Heo 
783edfaa7c3SKay Sievers static ssize_t disk_removable_show(struct device *dev,
784edfaa7c3SKay Sievers 				   struct device_attribute *attr, char *buf)
785a7fd6706SKay Sievers {
786edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
787a7fd6706SKay Sievers 
788edfaa7c3SKay Sievers 	return sprintf(buf, "%d\n",
7891da177e4SLinus Torvalds 		       (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0));
790edfaa7c3SKay Sievers }
7911da177e4SLinus Torvalds 
7921c9ce527SKay Sievers static ssize_t disk_ro_show(struct device *dev,
7931c9ce527SKay Sievers 				   struct device_attribute *attr, char *buf)
7941c9ce527SKay Sievers {
7951c9ce527SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
7961c9ce527SKay Sievers 
797b7db9956STejun Heo 	return sprintf(buf, "%d\n", get_disk_ro(disk) ? 1 : 0);
7981c9ce527SKay Sievers }
7991c9ce527SKay Sievers 
800edfaa7c3SKay Sievers static ssize_t disk_capability_show(struct device *dev,
801edfaa7c3SKay Sievers 				    struct device_attribute *attr, char *buf)
80286ce18d7SKristen Carlson Accardi {
803edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
804edfaa7c3SKay Sievers 
805edfaa7c3SKay Sievers 	return sprintf(buf, "%x\n", disk->flags);
80686ce18d7SKristen Carlson Accardi }
807edfaa7c3SKay Sievers 
808edfaa7c3SKay Sievers static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL);
8091f014290STejun Heo static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL);
810edfaa7c3SKay Sievers static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL);
8111c9ce527SKay Sievers static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL);
812e5610521STejun Heo static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL);
813edfaa7c3SKay Sievers static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL);
814074a7acaSTejun Heo static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL);
815c17bb495SAkinobu Mita #ifdef CONFIG_FAIL_MAKE_REQUEST
816edfaa7c3SKay Sievers static struct device_attribute dev_attr_fail =
817eddb2e26STejun Heo 	__ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store);
818c17bb495SAkinobu Mita #endif
819581d4e28SJens Axboe #ifdef CONFIG_FAIL_IO_TIMEOUT
820581d4e28SJens Axboe static struct device_attribute dev_attr_fail_timeout =
821581d4e28SJens Axboe 	__ATTR(io-timeout-fail,  S_IRUGO|S_IWUSR, part_timeout_show,
822581d4e28SJens Axboe 		part_timeout_store);
823581d4e28SJens Axboe #endif
824edfaa7c3SKay Sievers 
825edfaa7c3SKay Sievers static struct attribute *disk_attrs[] = {
826edfaa7c3SKay Sievers 	&dev_attr_range.attr,
8271f014290STejun Heo 	&dev_attr_ext_range.attr,
828edfaa7c3SKay Sievers 	&dev_attr_removable.attr,
8291c9ce527SKay Sievers 	&dev_attr_ro.attr,
830edfaa7c3SKay Sievers 	&dev_attr_size.attr,
831edfaa7c3SKay Sievers 	&dev_attr_capability.attr,
832edfaa7c3SKay Sievers 	&dev_attr_stat.attr,
833edfaa7c3SKay Sievers #ifdef CONFIG_FAIL_MAKE_REQUEST
834edfaa7c3SKay Sievers 	&dev_attr_fail.attr,
835edfaa7c3SKay Sievers #endif
836581d4e28SJens Axboe #ifdef CONFIG_FAIL_IO_TIMEOUT
837581d4e28SJens Axboe 	&dev_attr_fail_timeout.attr,
838581d4e28SJens Axboe #endif
839edfaa7c3SKay Sievers 	NULL
8401da177e4SLinus Torvalds };
8411da177e4SLinus Torvalds 
842edfaa7c3SKay Sievers static struct attribute_group disk_attr_group = {
843edfaa7c3SKay Sievers 	.attrs = disk_attrs,
844edfaa7c3SKay Sievers };
845edfaa7c3SKay Sievers 
846edfaa7c3SKay Sievers static struct attribute_group *disk_attr_groups[] = {
847edfaa7c3SKay Sievers 	&disk_attr_group,
848edfaa7c3SKay Sievers 	NULL
849edfaa7c3SKay Sievers };
850edfaa7c3SKay Sievers 
851540eed56STejun Heo static void disk_free_ptbl_rcu_cb(struct rcu_head *head)
852540eed56STejun Heo {
853540eed56STejun Heo 	struct disk_part_tbl *ptbl =
854540eed56STejun Heo 		container_of(head, struct disk_part_tbl, rcu_head);
855540eed56STejun Heo 
856540eed56STejun Heo 	kfree(ptbl);
857540eed56STejun Heo }
858540eed56STejun Heo 
859540eed56STejun Heo /**
860540eed56STejun Heo  * disk_replace_part_tbl - replace disk->part_tbl in RCU-safe way
861540eed56STejun Heo  * @disk: disk to replace part_tbl for
862540eed56STejun Heo  * @new_ptbl: new part_tbl to install
863540eed56STejun Heo  *
864540eed56STejun Heo  * Replace disk->part_tbl with @new_ptbl in RCU-safe way.  The
865540eed56STejun Heo  * original ptbl is freed using RCU callback.
866540eed56STejun Heo  *
867540eed56STejun Heo  * LOCKING:
868540eed56STejun Heo  * Matching bd_mutx locked.
869540eed56STejun Heo  */
870540eed56STejun Heo static void disk_replace_part_tbl(struct gendisk *disk,
871540eed56STejun Heo 				  struct disk_part_tbl *new_ptbl)
872540eed56STejun Heo {
873540eed56STejun Heo 	struct disk_part_tbl *old_ptbl = disk->part_tbl;
874540eed56STejun Heo 
875540eed56STejun Heo 	rcu_assign_pointer(disk->part_tbl, new_ptbl);
876540eed56STejun Heo 	if (old_ptbl)
877540eed56STejun Heo 		call_rcu(&old_ptbl->rcu_head, disk_free_ptbl_rcu_cb);
878540eed56STejun Heo }
879540eed56STejun Heo 
880540eed56STejun Heo /**
881540eed56STejun Heo  * disk_expand_part_tbl - expand disk->part_tbl
882540eed56STejun Heo  * @disk: disk to expand part_tbl for
883540eed56STejun Heo  * @partno: expand such that this partno can fit in
884540eed56STejun Heo  *
885540eed56STejun Heo  * Expand disk->part_tbl such that @partno can fit in.  disk->part_tbl
886540eed56STejun Heo  * uses RCU to allow unlocked dereferencing for stats and other stuff.
887540eed56STejun Heo  *
888540eed56STejun Heo  * LOCKING:
889540eed56STejun Heo  * Matching bd_mutex locked, might sleep.
890540eed56STejun Heo  *
891540eed56STejun Heo  * RETURNS:
892540eed56STejun Heo  * 0 on success, -errno on failure.
893540eed56STejun Heo  */
894540eed56STejun Heo int disk_expand_part_tbl(struct gendisk *disk, int partno)
895540eed56STejun Heo {
896540eed56STejun Heo 	struct disk_part_tbl *old_ptbl = disk->part_tbl;
897540eed56STejun Heo 	struct disk_part_tbl *new_ptbl;
898540eed56STejun Heo 	int len = old_ptbl ? old_ptbl->len : 0;
899540eed56STejun Heo 	int target = partno + 1;
900540eed56STejun Heo 	size_t size;
901540eed56STejun Heo 	int i;
902540eed56STejun Heo 
903540eed56STejun Heo 	/* disk_max_parts() is zero during initialization, ignore if so */
904540eed56STejun Heo 	if (disk_max_parts(disk) && target > disk_max_parts(disk))
905540eed56STejun Heo 		return -EINVAL;
906540eed56STejun Heo 
907540eed56STejun Heo 	if (target <= len)
908540eed56STejun Heo 		return 0;
909540eed56STejun Heo 
910540eed56STejun Heo 	size = sizeof(*new_ptbl) + target * sizeof(new_ptbl->part[0]);
911540eed56STejun Heo 	new_ptbl = kzalloc_node(size, GFP_KERNEL, disk->node_id);
912540eed56STejun Heo 	if (!new_ptbl)
913540eed56STejun Heo 		return -ENOMEM;
914540eed56STejun Heo 
915540eed56STejun Heo 	INIT_RCU_HEAD(&new_ptbl->rcu_head);
916540eed56STejun Heo 	new_ptbl->len = target;
917540eed56STejun Heo 
918540eed56STejun Heo 	for (i = 0; i < len; i++)
919540eed56STejun Heo 		rcu_assign_pointer(new_ptbl->part[i], old_ptbl->part[i]);
920540eed56STejun Heo 
921540eed56STejun Heo 	disk_replace_part_tbl(disk, new_ptbl);
922540eed56STejun Heo 	return 0;
923540eed56STejun Heo }
924540eed56STejun Heo 
925edfaa7c3SKay Sievers static void disk_release(struct device *dev)
9261da177e4SLinus Torvalds {
927edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
928edfaa7c3SKay Sievers 
9291da177e4SLinus Torvalds 	kfree(disk->random);
930540eed56STejun Heo 	disk_replace_part_tbl(disk, NULL);
931074a7acaSTejun Heo 	free_part_stats(&disk->part0);
9321da177e4SLinus Torvalds 	kfree(disk);
9331da177e4SLinus Torvalds }
934edfaa7c3SKay Sievers struct class block_class = {
935edfaa7c3SKay Sievers 	.name		= "block",
9361da177e4SLinus Torvalds };
9371da177e4SLinus Torvalds 
9381826eadfSAdrian Bunk static struct device_type disk_type = {
939edfaa7c3SKay Sievers 	.name		= "disk",
940edfaa7c3SKay Sievers 	.groups		= disk_attr_groups,
941edfaa7c3SKay Sievers 	.release	= disk_release,
9421da177e4SLinus Torvalds };
9431da177e4SLinus Torvalds 
944a6e2ba88SRandy Dunlap #ifdef CONFIG_PROC_FS
945cf771cb5STejun Heo /*
946cf771cb5STejun Heo  * aggregate disk stat collector.  Uses the same stats that the sysfs
947cf771cb5STejun Heo  * entries do, above, but makes them available through one seq_file.
948cf771cb5STejun Heo  *
949cf771cb5STejun Heo  * The output looks suspiciously like /proc/partitions with a bunch of
950cf771cb5STejun Heo  * extra fields.
951cf771cb5STejun Heo  */
952cf771cb5STejun Heo static int diskstats_show(struct seq_file *seqf, void *v)
9531da177e4SLinus Torvalds {
9541da177e4SLinus Torvalds 	struct gendisk *gp = v;
955e71bf0d0STejun Heo 	struct disk_part_iter piter;
956e71bf0d0STejun Heo 	struct hd_struct *hd;
9571da177e4SLinus Torvalds 	char buf[BDEVNAME_SIZE];
958c9959059STejun Heo 	int cpu;
9591da177e4SLinus Torvalds 
9601da177e4SLinus Torvalds 	/*
961ed9e1982STejun Heo 	if (&disk_to_dev(gp)->kobj.entry == block_class.devices.next)
962cf771cb5STejun Heo 		seq_puts(seqf,	"major minor name"
9631da177e4SLinus Torvalds 				"     rio rmerge rsect ruse wio wmerge "
9641da177e4SLinus Torvalds 				"wsect wuse running use aveq"
9651da177e4SLinus Torvalds 				"\n\n");
9661da177e4SLinus Torvalds 	*/
9671da177e4SLinus Torvalds 
968074a7acaSTejun Heo 	disk_part_iter_init(&piter, gp, DISK_PITER_INCL_PART0);
969e71bf0d0STejun Heo 	while ((hd = disk_part_iter_next(&piter))) {
970074a7acaSTejun Heo 		cpu = part_stat_lock();
971c9959059STejun Heo 		part_round_stats(cpu, hd);
972074a7acaSTejun Heo 		part_stat_unlock();
9731f014290STejun Heo 		seq_printf(seqf, "%4d %7d %s %lu %lu %llu "
97428f39d55SJerome Marchand 			   "%u %lu %lu %llu %u %u %u %u\n",
975f331c029STejun Heo 			   MAJOR(part_devt(hd)), MINOR(part_devt(hd)),
976f331c029STejun Heo 			   disk_name(gp, hd->partno, buf),
97728f39d55SJerome Marchand 			   part_stat_read(hd, ios[0]),
97828f39d55SJerome Marchand 			   part_stat_read(hd, merges[0]),
97928f39d55SJerome Marchand 			   (unsigned long long)part_stat_read(hd, sectors[0]),
98028f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, ticks[0])),
98128f39d55SJerome Marchand 			   part_stat_read(hd, ios[1]),
98228f39d55SJerome Marchand 			   part_stat_read(hd, merges[1]),
98328f39d55SJerome Marchand 			   (unsigned long long)part_stat_read(hd, sectors[1]),
98428f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, ticks[1])),
98528f39d55SJerome Marchand 			   hd->in_flight,
98628f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, io_ticks)),
98728f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, time_in_queue))
98828f39d55SJerome Marchand 			);
9891da177e4SLinus Torvalds 	}
990e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
9911da177e4SLinus Torvalds 
9921da177e4SLinus Torvalds 	return 0;
9931da177e4SLinus Torvalds }
9941da177e4SLinus Torvalds 
99512f32bb3SJan Engelhardt const struct seq_operations diskstats_op = {
996def4e38dSTejun Heo 	.start	= disk_seqf_start,
997def4e38dSTejun Heo 	.next	= disk_seqf_next,
998def4e38dSTejun Heo 	.stop	= disk_seqf_stop,
9991da177e4SLinus Torvalds 	.show	= diskstats_show
10001da177e4SLinus Torvalds };
1001a6e2ba88SRandy Dunlap #endif /* CONFIG_PROC_FS */
10021da177e4SLinus Torvalds 
10038ce7ad7bSKristen Carlson Accardi static void media_change_notify_thread(struct work_struct *work)
10048ce7ad7bSKristen Carlson Accardi {
10058ce7ad7bSKristen Carlson Accardi 	struct gendisk *gd = container_of(work, struct gendisk, async_notify);
10068ce7ad7bSKristen Carlson Accardi 	char event[] = "MEDIA_CHANGE=1";
10078ce7ad7bSKristen Carlson Accardi 	char *envp[] = { event, NULL };
10088ce7ad7bSKristen Carlson Accardi 
10098ce7ad7bSKristen Carlson Accardi 	/*
10108ce7ad7bSKristen Carlson Accardi 	 * set enviroment vars to indicate which event this is for
10118ce7ad7bSKristen Carlson Accardi 	 * so that user space will know to go check the media status.
10128ce7ad7bSKristen Carlson Accardi 	 */
1013ed9e1982STejun Heo 	kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp);
10148ce7ad7bSKristen Carlson Accardi 	put_device(gd->driverfs_dev);
10158ce7ad7bSKristen Carlson Accardi }
10168ce7ad7bSKristen Carlson Accardi 
10171826eadfSAdrian Bunk #if 0
10188ce7ad7bSKristen Carlson Accardi void genhd_media_change_notify(struct gendisk *disk)
10198ce7ad7bSKristen Carlson Accardi {
10208ce7ad7bSKristen Carlson Accardi 	get_device(disk->driverfs_dev);
10218ce7ad7bSKristen Carlson Accardi 	schedule_work(&disk->async_notify);
10228ce7ad7bSKristen Carlson Accardi }
10238ce7ad7bSKristen Carlson Accardi EXPORT_SYMBOL_GPL(genhd_media_change_notify);
10241826eadfSAdrian Bunk #endif  /*  0  */
10258ce7ad7bSKristen Carlson Accardi 
1026cf771cb5STejun Heo dev_t blk_lookup_devt(const char *name, int partno)
1027edfaa7c3SKay Sievers {
1028edfaa7c3SKay Sievers 	dev_t devt = MKDEV(0, 0);
1029def4e38dSTejun Heo 	struct class_dev_iter iter;
1030def4e38dSTejun Heo 	struct device *dev;
1031edfaa7c3SKay Sievers 
1032def4e38dSTejun Heo 	class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
1033def4e38dSTejun Heo 	while ((dev = class_dev_iter_next(&iter))) {
1034def4e38dSTejun Heo 		struct gendisk *disk = dev_to_disk(dev);
1035548b10ebSTejun Heo 		struct hd_struct *part;
1036def4e38dSTejun Heo 
1037f331c029STejun Heo 		if (strcmp(dev->bus_id, name))
1038f331c029STejun Heo 			continue;
1039f331c029STejun Heo 
1040e71bf0d0STejun Heo 		part = disk_get_part(disk, partno);
10412bbedcb4STejun Heo 		if (part) {
1042f331c029STejun Heo 			devt = part_devt(part);
1043e71bf0d0STejun Heo 			disk_put_part(part);
1044f331c029STejun Heo 			break;
1045def4e38dSTejun Heo 		}
1046548b10ebSTejun Heo 		disk_put_part(part);
1047548b10ebSTejun Heo 	}
1048def4e38dSTejun Heo 	class_dev_iter_exit(&iter);
1049edfaa7c3SKay Sievers 	return devt;
1050edfaa7c3SKay Sievers }
1051edfaa7c3SKay Sievers EXPORT_SYMBOL(blk_lookup_devt);
1052edfaa7c3SKay Sievers 
10531da177e4SLinus Torvalds struct gendisk *alloc_disk(int minors)
10541da177e4SLinus Torvalds {
10551946089aSChristoph Lameter 	return alloc_disk_node(minors, -1);
10561946089aSChristoph Lameter }
1057689d6facSTejun Heo EXPORT_SYMBOL(alloc_disk);
10581946089aSChristoph Lameter 
10591946089aSChristoph Lameter struct gendisk *alloc_disk_node(int minors, int node_id)
10601946089aSChristoph Lameter {
10611946089aSChristoph Lameter 	struct gendisk *disk;
10621946089aSChristoph Lameter 
106394f6030cSChristoph Lameter 	disk = kmalloc_node(sizeof(struct gendisk),
106494f6030cSChristoph Lameter 				GFP_KERNEL | __GFP_ZERO, node_id);
10651da177e4SLinus Torvalds 	if (disk) {
1066074a7acaSTejun Heo 		if (!init_part_stats(&disk->part0)) {
10671da177e4SLinus Torvalds 			kfree(disk);
10681da177e4SLinus Torvalds 			return NULL;
10691da177e4SLinus Torvalds 		}
1070540eed56STejun Heo 		if (disk_expand_part_tbl(disk, 0)) {
1071074a7acaSTejun Heo 			free_part_stats(&disk->part0);
10721da177e4SLinus Torvalds 			kfree(disk);
10731da177e4SLinus Torvalds 			return NULL;
10741da177e4SLinus Torvalds 		}
1075540eed56STejun Heo 		disk->part_tbl->part[0] = &disk->part0;
1076b5d0b9dfSTejun Heo 
10771da177e4SLinus Torvalds 		disk->minors = minors;
10781da177e4SLinus Torvalds 		rand_initialize_disk(disk);
1079ed9e1982STejun Heo 		disk_to_dev(disk)->class = &block_class;
1080ed9e1982STejun Heo 		disk_to_dev(disk)->type = &disk_type;
1081ed9e1982STejun Heo 		device_initialize(disk_to_dev(disk));
10828ce7ad7bSKristen Carlson Accardi 		INIT_WORK(&disk->async_notify,
10838ce7ad7bSKristen Carlson Accardi 			media_change_notify_thread);
1084540eed56STejun Heo 		disk->node_id = node_id;
10851da177e4SLinus Torvalds 	}
10861da177e4SLinus Torvalds 	return disk;
10871da177e4SLinus Torvalds }
10881946089aSChristoph Lameter EXPORT_SYMBOL(alloc_disk_node);
10891da177e4SLinus Torvalds 
10901da177e4SLinus Torvalds struct kobject *get_disk(struct gendisk *disk)
10911da177e4SLinus Torvalds {
10921da177e4SLinus Torvalds 	struct module *owner;
10931da177e4SLinus Torvalds 	struct kobject *kobj;
10941da177e4SLinus Torvalds 
10951da177e4SLinus Torvalds 	if (!disk->fops)
10961da177e4SLinus Torvalds 		return NULL;
10971da177e4SLinus Torvalds 	owner = disk->fops->owner;
10981da177e4SLinus Torvalds 	if (owner && !try_module_get(owner))
10991da177e4SLinus Torvalds 		return NULL;
1100ed9e1982STejun Heo 	kobj = kobject_get(&disk_to_dev(disk)->kobj);
11011da177e4SLinus Torvalds 	if (kobj == NULL) {
11021da177e4SLinus Torvalds 		module_put(owner);
11031da177e4SLinus Torvalds 		return NULL;
11041da177e4SLinus Torvalds 	}
11051da177e4SLinus Torvalds 	return kobj;
11061da177e4SLinus Torvalds 
11071da177e4SLinus Torvalds }
11081da177e4SLinus Torvalds 
11091da177e4SLinus Torvalds EXPORT_SYMBOL(get_disk);
11101da177e4SLinus Torvalds 
11111da177e4SLinus Torvalds void put_disk(struct gendisk *disk)
11121da177e4SLinus Torvalds {
11131da177e4SLinus Torvalds 	if (disk)
1114ed9e1982STejun Heo 		kobject_put(&disk_to_dev(disk)->kobj);
11151da177e4SLinus Torvalds }
11161da177e4SLinus Torvalds 
11171da177e4SLinus Torvalds EXPORT_SYMBOL(put_disk);
11181da177e4SLinus Torvalds 
11191da177e4SLinus Torvalds void set_device_ro(struct block_device *bdev, int flag)
11201da177e4SLinus Torvalds {
11211da177e4SLinus Torvalds 	bdev->bd_part->policy = flag;
11221da177e4SLinus Torvalds }
11231da177e4SLinus Torvalds 
11241da177e4SLinus Torvalds EXPORT_SYMBOL(set_device_ro);
11251da177e4SLinus Torvalds 
11261da177e4SLinus Torvalds void set_disk_ro(struct gendisk *disk, int flag)
11271da177e4SLinus Torvalds {
1128e71bf0d0STejun Heo 	struct disk_part_iter piter;
1129e71bf0d0STejun Heo 	struct hd_struct *part;
1130e71bf0d0STejun Heo 
1131b7db9956STejun Heo 	disk_part_iter_init(&piter, disk,
1132b7db9956STejun Heo 			    DISK_PITER_INCL_EMPTY | DISK_PITER_INCL_PART0);
1133e71bf0d0STejun Heo 	while ((part = disk_part_iter_next(&piter)))
1134e71bf0d0STejun Heo 		part->policy = flag;
1135e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
11361da177e4SLinus Torvalds }
11371da177e4SLinus Torvalds 
11381da177e4SLinus Torvalds EXPORT_SYMBOL(set_disk_ro);
11391da177e4SLinus Torvalds 
11401da177e4SLinus Torvalds int bdev_read_only(struct block_device *bdev)
11411da177e4SLinus Torvalds {
11421da177e4SLinus Torvalds 	if (!bdev)
11431da177e4SLinus Torvalds 		return 0;
11441da177e4SLinus Torvalds 	return bdev->bd_part->policy;
11451da177e4SLinus Torvalds }
11461da177e4SLinus Torvalds 
11471da177e4SLinus Torvalds EXPORT_SYMBOL(bdev_read_only);
11481da177e4SLinus Torvalds 
1149cf771cb5STejun Heo int invalidate_partition(struct gendisk *disk, int partno)
11501da177e4SLinus Torvalds {
11511da177e4SLinus Torvalds 	int res = 0;
1152cf771cb5STejun Heo 	struct block_device *bdev = bdget_disk(disk, partno);
11531da177e4SLinus Torvalds 	if (bdev) {
11542ef41634SChristoph Hellwig 		fsync_bdev(bdev);
11552ef41634SChristoph Hellwig 		res = __invalidate_device(bdev);
11561da177e4SLinus Torvalds 		bdput(bdev);
11571da177e4SLinus Torvalds 	}
11581da177e4SLinus Torvalds 	return res;
11591da177e4SLinus Torvalds }
11601da177e4SLinus Torvalds 
11611da177e4SLinus Torvalds EXPORT_SYMBOL(invalidate_partition);
1162