xref: /linux/block/genhd.c (revision 540eed5637b766bb1e881ef744c42617760b4815)
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 {
55*540eed56STejun Heo 	struct hd_struct *part = NULL;
56*540eed56STejun Heo 	struct disk_part_tbl *ptbl;
57e71bf0d0STejun Heo 
58*540eed56STejun Heo 	if (unlikely(partno < 0))
59e71bf0d0STejun Heo 		return NULL;
60*540eed56STejun Heo 
61e71bf0d0STejun Heo 	rcu_read_lock();
62*540eed56STejun Heo 
63*540eed56STejun Heo 	ptbl = rcu_dereference(disk->part_tbl);
64*540eed56STejun Heo 	if (likely(partno < ptbl->len)) {
65*540eed56STejun Heo 		part = rcu_dereference(ptbl->part[partno]);
66e71bf0d0STejun Heo 		if (part)
67ed9e1982STejun Heo 			get_device(part_to_dev(part));
68*540eed56STejun Heo 	}
69*540eed56STejun 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 {
90*540eed56STejun Heo 	struct disk_part_tbl *ptbl;
91*540eed56STejun Heo 
92*540eed56STejun Heo 	rcu_read_lock();
93*540eed56STejun Heo 	ptbl = rcu_dereference(disk->part_tbl);
94*540eed56STejun Heo 
95e71bf0d0STejun Heo 	piter->disk = disk;
96e71bf0d0STejun Heo 	piter->part = NULL;
97e71bf0d0STejun Heo 
98e71bf0d0STejun Heo 	if (flags & DISK_PITER_REVERSE)
99*540eed56STejun 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;
106*540eed56STejun Heo 
107*540eed56STejun 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 {
122*540eed56STejun 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 
129*540eed56STejun Heo 	/* get part_tbl */
130e71bf0d0STejun Heo 	rcu_read_lock();
131*540eed56STejun 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;
142*540eed56STejun 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 
149*540eed56STejun 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 {
200*540eed56STejun Heo 	struct disk_part_tbl *ptbl;
201e71bf0d0STejun Heo 	int i;
202e71bf0d0STejun Heo 
203*540eed56STejun Heo 	ptbl = rcu_dereference(disk->part_tbl);
204*540eed56STejun Heo 
205*540eed56STejun Heo 	for (i = 1; i < ptbl->len; i++) {
206*540eed56STejun 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.
4811da177e4SLinus Torvalds  */
4821da177e4SLinus Torvalds void add_disk(struct gendisk *disk)
4831da177e4SLinus Torvalds {
484cf0ca9feSPeter Zijlstra 	struct backing_dev_info *bdi;
4856ffeea77SGreg Kroah-Hartman 	int retval;
486cf0ca9feSPeter Zijlstra 
4871da177e4SLinus Torvalds 	disk->flags |= GENHD_FL_UP;
488ed9e1982STejun Heo 	disk_to_dev(disk)->devt = MKDEV(disk->major, disk->first_minor);
489f331c029STejun Heo 	blk_register_region(disk_devt(disk), disk->minors, NULL,
490f331c029STejun Heo 			    exact_match, exact_lock, disk);
4911da177e4SLinus Torvalds 	register_disk(disk);
4921da177e4SLinus Torvalds 	blk_register_queue(disk);
493cf0ca9feSPeter Zijlstra 
494cf0ca9feSPeter Zijlstra 	bdi = &disk->queue->backing_dev_info;
495f331c029STejun Heo 	bdi_register_dev(bdi, disk_devt(disk));
496ed9e1982STejun Heo 	retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj,
497ed9e1982STejun Heo 				   "bdi");
4986ffeea77SGreg Kroah-Hartman 	WARN_ON(retval);
4991da177e4SLinus Torvalds }
5001da177e4SLinus Torvalds 
5011da177e4SLinus Torvalds EXPORT_SYMBOL(add_disk);
5021da177e4SLinus Torvalds EXPORT_SYMBOL(del_gendisk);	/* in partitions/check.c */
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds void unlink_gendisk(struct gendisk *disk)
5051da177e4SLinus Torvalds {
506ed9e1982STejun Heo 	sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi");
507cf0ca9feSPeter Zijlstra 	bdi_unregister(&disk->queue->backing_dev_info);
5081da177e4SLinus Torvalds 	blk_unregister_queue(disk);
509f331c029STejun Heo 	blk_unregister_region(disk_devt(disk), disk->minors);
5101da177e4SLinus Torvalds }
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds /**
5131da177e4SLinus Torvalds  * get_gendisk - get partitioning information for a given device
514710027a4SRandy Dunlap  * @devt: device to get partitioning information for
515710027a4SRandy Dunlap  * @part: returned partition index
5161da177e4SLinus Torvalds  *
5171da177e4SLinus Torvalds  * This function gets the structure containing partitioning
518710027a4SRandy Dunlap  * information for the given device @devt.
5191da177e4SLinus Torvalds  */
520cf771cb5STejun Heo struct gendisk *get_gendisk(dev_t devt, int *partno)
5211da177e4SLinus Torvalds {
522bcce3de1STejun Heo 	struct gendisk *disk = NULL;
523edfaa7c3SKay Sievers 
524bcce3de1STejun Heo 	if (MAJOR(devt) != BLOCK_EXT_MAJOR) {
525bcce3de1STejun Heo 		struct kobject *kobj;
526bcce3de1STejun Heo 
527bcce3de1STejun Heo 		kobj = kobj_lookup(bdev_map, devt, partno);
528bcce3de1STejun Heo 		if (kobj)
529bcce3de1STejun Heo 			disk = dev_to_disk(kobj_to_dev(kobj));
530bcce3de1STejun Heo 	} else {
531bcce3de1STejun Heo 		struct hd_struct *part;
532bcce3de1STejun Heo 
533bcce3de1STejun Heo 		mutex_lock(&ext_devt_mutex);
534870d6656STejun Heo 		part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
535bcce3de1STejun Heo 		if (part && get_disk(part_to_disk(part))) {
536bcce3de1STejun Heo 			*partno = part->partno;
537bcce3de1STejun Heo 			disk = part_to_disk(part);
538bcce3de1STejun Heo 		}
539bcce3de1STejun Heo 		mutex_unlock(&ext_devt_mutex);
540bcce3de1STejun Heo 	}
541bcce3de1STejun Heo 
542bcce3de1STejun Heo 	return disk;
5431da177e4SLinus Torvalds }
5441da177e4SLinus Torvalds 
545f331c029STejun Heo /**
546f331c029STejun Heo  * bdget_disk - do bdget() by gendisk and partition number
547f331c029STejun Heo  * @disk: gendisk of interest
548f331c029STejun Heo  * @partno: partition number
549f331c029STejun Heo  *
550f331c029STejun Heo  * Find partition @partno from @disk, do bdget() on it.
551f331c029STejun Heo  *
552f331c029STejun Heo  * CONTEXT:
553f331c029STejun Heo  * Don't care.
554f331c029STejun Heo  *
555f331c029STejun Heo  * RETURNS:
556f331c029STejun Heo  * Resulting block_device on success, NULL on failure.
557f331c029STejun Heo  */
558f331c029STejun Heo extern struct block_device *bdget_disk(struct gendisk *disk, int partno)
559f331c029STejun Heo {
560e71bf0d0STejun Heo 	struct hd_struct *part;
561548b10ebSTejun Heo 	struct block_device *bdev = NULL;
562f331c029STejun Heo 
563e71bf0d0STejun Heo 	part = disk_get_part(disk, partno);
564548b10ebSTejun Heo 	if (part && (part->nr_sects || partno == 0))
565548b10ebSTejun Heo 		bdev = bdget(part_devt(part));
566e71bf0d0STejun Heo 	disk_put_part(part);
567f331c029STejun Heo 
568548b10ebSTejun Heo 	return bdev;
569f331c029STejun Heo }
570f331c029STejun Heo EXPORT_SYMBOL(bdget_disk);
571f331c029STejun Heo 
572dd2a345fSDave Gilbert /*
5735c6f35c5SGreg Kroah-Hartman  * print a full list of all partitions - intended for places where the root
5745c6f35c5SGreg Kroah-Hartman  * filesystem can't be mounted and thus to give the victim some idea of what
5755c6f35c5SGreg Kroah-Hartman  * went wrong
5765c6f35c5SGreg Kroah-Hartman  */
5775c6f35c5SGreg Kroah-Hartman void __init printk_all_partitions(void)
5785c6f35c5SGreg Kroah-Hartman {
579def4e38dSTejun Heo 	struct class_dev_iter iter;
580def4e38dSTejun Heo 	struct device *dev;
581def4e38dSTejun Heo 
582def4e38dSTejun Heo 	class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
583def4e38dSTejun Heo 	while ((dev = class_dev_iter_next(&iter))) {
584def4e38dSTejun Heo 		struct gendisk *disk = dev_to_disk(dev);
585e71bf0d0STejun Heo 		struct disk_part_iter piter;
586e71bf0d0STejun Heo 		struct hd_struct *part;
5871f014290STejun Heo 		char name_buf[BDEVNAME_SIZE];
5881f014290STejun Heo 		char devt_buf[BDEVT_SIZE];
589def4e38dSTejun Heo 
590def4e38dSTejun Heo 		/*
591def4e38dSTejun Heo 		 * Don't show empty devices or things that have been
592def4e38dSTejun Heo 		 * surpressed
593def4e38dSTejun Heo 		 */
594def4e38dSTejun Heo 		if (get_capacity(disk) == 0 ||
595def4e38dSTejun Heo 		    (disk->flags & GENHD_FL_SUPPRESS_PARTITION_INFO))
596def4e38dSTejun Heo 			continue;
597def4e38dSTejun Heo 
598def4e38dSTejun Heo 		/*
599def4e38dSTejun Heo 		 * Note, unlike /proc/partitions, I am showing the
600def4e38dSTejun Heo 		 * numbers in hex - the same format as the root=
601def4e38dSTejun Heo 		 * option takes.
602def4e38dSTejun Heo 		 */
603074a7acaSTejun Heo 		disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0);
604074a7acaSTejun Heo 		while ((part = disk_part_iter_next(&piter))) {
605074a7acaSTejun Heo 			bool is_part0 = part == &disk->part0;
606074a7acaSTejun Heo 
607074a7acaSTejun Heo 			printk("%s%s %10llu %s", is_part0 ? "" : "  ",
608074a7acaSTejun Heo 			       bdevt_str(part_devt(part), devt_buf),
609074a7acaSTejun Heo 			       (unsigned long long)part->nr_sects >> 1,
610074a7acaSTejun Heo 			       disk_name(disk, part->partno, name_buf));
611074a7acaSTejun Heo 			if (is_part0) {
612def4e38dSTejun Heo 				if (disk->driverfs_dev != NULL &&
613def4e38dSTejun Heo 				    disk->driverfs_dev->driver != NULL)
614def4e38dSTejun Heo 					printk(" driver: %s\n",
615def4e38dSTejun Heo 					      disk->driverfs_dev->driver->name);
616def4e38dSTejun Heo 				else
617def4e38dSTejun Heo 					printk(" (driver?)\n");
618074a7acaSTejun Heo 			} else
619074a7acaSTejun Heo 				printk("\n");
620074a7acaSTejun Heo 		}
621e71bf0d0STejun Heo 		disk_part_iter_exit(&piter);
622def4e38dSTejun Heo 	}
623def4e38dSTejun Heo 	class_dev_iter_exit(&iter);
624dd2a345fSDave Gilbert }
625dd2a345fSDave Gilbert 
6261da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
6271da177e4SLinus Torvalds /* iterator */
628def4e38dSTejun Heo static void *disk_seqf_start(struct seq_file *seqf, loff_t *pos)
62968c4d4a7SGreg Kroah-Hartman {
630def4e38dSTejun Heo 	loff_t skip = *pos;
631def4e38dSTejun Heo 	struct class_dev_iter *iter;
632def4e38dSTejun Heo 	struct device *dev;
63368c4d4a7SGreg Kroah-Hartman 
634def4e38dSTejun Heo 	iter = kmalloc(GFP_KERNEL, sizeof(*iter));
635def4e38dSTejun Heo 	if (!iter)
636def4e38dSTejun Heo 		return ERR_PTR(-ENOMEM);
637def4e38dSTejun Heo 
638def4e38dSTejun Heo 	seqf->private = iter;
639def4e38dSTejun Heo 	class_dev_iter_init(iter, &block_class, NULL, &disk_type);
640def4e38dSTejun Heo 	do {
641def4e38dSTejun Heo 		dev = class_dev_iter_next(iter);
642def4e38dSTejun Heo 		if (!dev)
643def4e38dSTejun Heo 			return NULL;
644def4e38dSTejun Heo 	} while (skip--);
645def4e38dSTejun Heo 
646def4e38dSTejun Heo 	return dev_to_disk(dev);
64768c4d4a7SGreg Kroah-Hartman }
64868c4d4a7SGreg Kroah-Hartman 
649def4e38dSTejun Heo static void *disk_seqf_next(struct seq_file *seqf, void *v, loff_t *pos)
6501da177e4SLinus Torvalds {
651edfaa7c3SKay Sievers 	struct device *dev;
65266c64afeSGreg Kroah-Hartman 
653def4e38dSTejun Heo 	(*pos)++;
654def4e38dSTejun Heo 	dev = class_dev_iter_next(seqf->private);
6552ac3cee5STejun Heo 	if (dev)
656edfaa7c3SKay Sievers 		return dev_to_disk(dev);
6572ac3cee5STejun Heo 
6581da177e4SLinus Torvalds 	return NULL;
6591da177e4SLinus Torvalds }
6601da177e4SLinus Torvalds 
661def4e38dSTejun Heo static void disk_seqf_stop(struct seq_file *seqf, void *v)
66227f30251SGreg Kroah-Hartman {
663def4e38dSTejun Heo 	struct class_dev_iter *iter = seqf->private;
664def4e38dSTejun Heo 
665def4e38dSTejun Heo 	/* stop is called even after start failed :-( */
666def4e38dSTejun Heo 	if (iter) {
667def4e38dSTejun Heo 		class_dev_iter_exit(iter);
668def4e38dSTejun Heo 		kfree(iter);
669def4e38dSTejun Heo 	}
67027f30251SGreg Kroah-Hartman }
67127f30251SGreg Kroah-Hartman 
672def4e38dSTejun Heo static void *show_partition_start(struct seq_file *seqf, loff_t *pos)
6731da177e4SLinus Torvalds {
674def4e38dSTejun Heo 	static void *p;
6751da177e4SLinus Torvalds 
676def4e38dSTejun Heo 	p = disk_seqf_start(seqf, pos);
677def4e38dSTejun Heo 	if (!IS_ERR(p) && p)
678def4e38dSTejun Heo 		seq_puts(seqf, "major minor  #blocks  name\n\n");
679def4e38dSTejun Heo 	return p;
6801da177e4SLinus Torvalds }
6811da177e4SLinus Torvalds 
682cf771cb5STejun Heo static int show_partition(struct seq_file *seqf, void *v)
6831da177e4SLinus Torvalds {
6841da177e4SLinus Torvalds 	struct gendisk *sgp = v;
685e71bf0d0STejun Heo 	struct disk_part_iter piter;
686e71bf0d0STejun Heo 	struct hd_struct *part;
6871da177e4SLinus Torvalds 	char buf[BDEVNAME_SIZE];
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds 	/* Don't show non-partitionable removeable devices or empty devices */
690b5d0b9dfSTejun Heo 	if (!get_capacity(sgp) || (!disk_partitionable(sgp) &&
691f331c029STejun Heo 				   (sgp->flags & GENHD_FL_REMOVABLE)))
6921da177e4SLinus Torvalds 		return 0;
6931da177e4SLinus Torvalds 	if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO)
6941da177e4SLinus Torvalds 		return 0;
6951da177e4SLinus Torvalds 
6961da177e4SLinus Torvalds 	/* show the full disk and all non-0 size partitions of it */
697074a7acaSTejun Heo 	disk_part_iter_init(&piter, sgp, DISK_PITER_INCL_PART0);
698e71bf0d0STejun Heo 	while ((part = disk_part_iter_next(&piter)))
6991f014290STejun Heo 		seq_printf(seqf, "%4d  %7d %10llu %s\n",
700f331c029STejun Heo 			   MAJOR(part_devt(part)), MINOR(part_devt(part)),
701f331c029STejun Heo 			   (unsigned long long)part->nr_sects >> 1,
702f331c029STejun Heo 			   disk_name(sgp, part->partno, buf));
703e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
7041da177e4SLinus Torvalds 
7051da177e4SLinus Torvalds 	return 0;
7061da177e4SLinus Torvalds }
7071da177e4SLinus Torvalds 
70812f32bb3SJan Engelhardt const struct seq_operations partitions_op = {
709def4e38dSTejun Heo 	.start	= show_partition_start,
710def4e38dSTejun Heo 	.next	= disk_seqf_next,
711def4e38dSTejun Heo 	.stop	= disk_seqf_stop,
7121da177e4SLinus Torvalds 	.show	= show_partition
7131da177e4SLinus Torvalds };
7141da177e4SLinus Torvalds #endif
7151da177e4SLinus Torvalds 
7161da177e4SLinus Torvalds 
717cf771cb5STejun Heo static struct kobject *base_probe(dev_t devt, int *partno, void *data)
7181da177e4SLinus Torvalds {
719edfaa7c3SKay Sievers 	if (request_module("block-major-%d-%d", MAJOR(devt), MINOR(devt)) > 0)
7201da177e4SLinus Torvalds 		/* Make old-style 2.4 aliases work */
721edfaa7c3SKay Sievers 		request_module("block-major-%d", MAJOR(devt));
7221da177e4SLinus Torvalds 	return NULL;
7231da177e4SLinus Torvalds }
7241da177e4SLinus Torvalds 
7251da177e4SLinus Torvalds static int __init genhd_device_init(void)
7261da177e4SLinus Torvalds {
727e105b8bfSDan Williams 	int error;
728e105b8bfSDan Williams 
729e105b8bfSDan Williams 	block_class.dev_kobj = sysfs_dev_block_kobj;
730e105b8bfSDan Williams 	error = class_register(&block_class);
731ee27a558SRoland McGrath 	if (unlikely(error))
732ee27a558SRoland McGrath 		return error;
733edfaa7c3SKay Sievers 	bdev_map = kobj_map_init(base_probe, &block_class_lock);
7341da177e4SLinus Torvalds 	blk_dev_init();
735edfaa7c3SKay Sievers 
736edfaa7c3SKay Sievers #ifndef CONFIG_SYSFS_DEPRECATED
737edfaa7c3SKay Sievers 	/* create top-level block dir */
738edfaa7c3SKay Sievers 	block_depr = kobject_create_and_add("block", NULL);
739edfaa7c3SKay Sievers #endif
740830d3cfbSGreg Kroah-Hartman 	return 0;
7411da177e4SLinus Torvalds }
7421da177e4SLinus Torvalds 
7431da177e4SLinus Torvalds subsys_initcall(genhd_device_init);
7441da177e4SLinus Torvalds 
745edfaa7c3SKay Sievers static ssize_t disk_range_show(struct device *dev,
746edfaa7c3SKay Sievers 			       struct device_attribute *attr, char *buf)
7471da177e4SLinus Torvalds {
748edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
7491da177e4SLinus Torvalds 
750edfaa7c3SKay Sievers 	return sprintf(buf, "%d\n", disk->minors);
7511da177e4SLinus Torvalds }
7521da177e4SLinus Torvalds 
7531f014290STejun Heo static ssize_t disk_ext_range_show(struct device *dev,
7541f014290STejun Heo 				   struct device_attribute *attr, char *buf)
7551f014290STejun Heo {
7561f014290STejun Heo 	struct gendisk *disk = dev_to_disk(dev);
7571f014290STejun Heo 
758b5d0b9dfSTejun Heo 	return sprintf(buf, "%d\n", disk_max_parts(disk));
7591f014290STejun Heo }
7601f014290STejun Heo 
761edfaa7c3SKay Sievers static ssize_t disk_removable_show(struct device *dev,
762edfaa7c3SKay Sievers 				   struct device_attribute *attr, char *buf)
763a7fd6706SKay Sievers {
764edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
765a7fd6706SKay Sievers 
766edfaa7c3SKay Sievers 	return sprintf(buf, "%d\n",
7671da177e4SLinus Torvalds 		       (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0));
768edfaa7c3SKay Sievers }
7691da177e4SLinus Torvalds 
7701c9ce527SKay Sievers static ssize_t disk_ro_show(struct device *dev,
7711c9ce527SKay Sievers 				   struct device_attribute *attr, char *buf)
7721c9ce527SKay Sievers {
7731c9ce527SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
7741c9ce527SKay Sievers 
775b7db9956STejun Heo 	return sprintf(buf, "%d\n", get_disk_ro(disk) ? 1 : 0);
7761c9ce527SKay Sievers }
7771c9ce527SKay Sievers 
778edfaa7c3SKay Sievers static ssize_t disk_capability_show(struct device *dev,
779edfaa7c3SKay Sievers 				    struct device_attribute *attr, char *buf)
78086ce18d7SKristen Carlson Accardi {
781edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
782edfaa7c3SKay Sievers 
783edfaa7c3SKay Sievers 	return sprintf(buf, "%x\n", disk->flags);
78486ce18d7SKristen Carlson Accardi }
785edfaa7c3SKay Sievers 
786edfaa7c3SKay Sievers static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL);
7871f014290STejun Heo static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL);
788edfaa7c3SKay Sievers static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL);
7891c9ce527SKay Sievers static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL);
790e5610521STejun Heo static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL);
791edfaa7c3SKay Sievers static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL);
792074a7acaSTejun Heo static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL);
793c17bb495SAkinobu Mita #ifdef CONFIG_FAIL_MAKE_REQUEST
794edfaa7c3SKay Sievers static struct device_attribute dev_attr_fail =
795eddb2e26STejun Heo 	__ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store);
796c17bb495SAkinobu Mita #endif
797edfaa7c3SKay Sievers 
798edfaa7c3SKay Sievers static struct attribute *disk_attrs[] = {
799edfaa7c3SKay Sievers 	&dev_attr_range.attr,
8001f014290STejun Heo 	&dev_attr_ext_range.attr,
801edfaa7c3SKay Sievers 	&dev_attr_removable.attr,
8021c9ce527SKay Sievers 	&dev_attr_ro.attr,
803edfaa7c3SKay Sievers 	&dev_attr_size.attr,
804edfaa7c3SKay Sievers 	&dev_attr_capability.attr,
805edfaa7c3SKay Sievers 	&dev_attr_stat.attr,
806edfaa7c3SKay Sievers #ifdef CONFIG_FAIL_MAKE_REQUEST
807edfaa7c3SKay Sievers 	&dev_attr_fail.attr,
808edfaa7c3SKay Sievers #endif
809edfaa7c3SKay Sievers 	NULL
8101da177e4SLinus Torvalds };
8111da177e4SLinus Torvalds 
812edfaa7c3SKay Sievers static struct attribute_group disk_attr_group = {
813edfaa7c3SKay Sievers 	.attrs = disk_attrs,
814edfaa7c3SKay Sievers };
815edfaa7c3SKay Sievers 
816edfaa7c3SKay Sievers static struct attribute_group *disk_attr_groups[] = {
817edfaa7c3SKay Sievers 	&disk_attr_group,
818edfaa7c3SKay Sievers 	NULL
819edfaa7c3SKay Sievers };
820edfaa7c3SKay Sievers 
821*540eed56STejun Heo static void disk_free_ptbl_rcu_cb(struct rcu_head *head)
822*540eed56STejun Heo {
823*540eed56STejun Heo 	struct disk_part_tbl *ptbl =
824*540eed56STejun Heo 		container_of(head, struct disk_part_tbl, rcu_head);
825*540eed56STejun Heo 
826*540eed56STejun Heo 	kfree(ptbl);
827*540eed56STejun Heo }
828*540eed56STejun Heo 
829*540eed56STejun Heo /**
830*540eed56STejun Heo  * disk_replace_part_tbl - replace disk->part_tbl in RCU-safe way
831*540eed56STejun Heo  * @disk: disk to replace part_tbl for
832*540eed56STejun Heo  * @new_ptbl: new part_tbl to install
833*540eed56STejun Heo  *
834*540eed56STejun Heo  * Replace disk->part_tbl with @new_ptbl in RCU-safe way.  The
835*540eed56STejun Heo  * original ptbl is freed using RCU callback.
836*540eed56STejun Heo  *
837*540eed56STejun Heo  * LOCKING:
838*540eed56STejun Heo  * Matching bd_mutx locked.
839*540eed56STejun Heo  */
840*540eed56STejun Heo static void disk_replace_part_tbl(struct gendisk *disk,
841*540eed56STejun Heo 				  struct disk_part_tbl *new_ptbl)
842*540eed56STejun Heo {
843*540eed56STejun Heo 	struct disk_part_tbl *old_ptbl = disk->part_tbl;
844*540eed56STejun Heo 
845*540eed56STejun Heo 	rcu_assign_pointer(disk->part_tbl, new_ptbl);
846*540eed56STejun Heo 	if (old_ptbl)
847*540eed56STejun Heo 		call_rcu(&old_ptbl->rcu_head, disk_free_ptbl_rcu_cb);
848*540eed56STejun Heo }
849*540eed56STejun Heo 
850*540eed56STejun Heo /**
851*540eed56STejun Heo  * disk_expand_part_tbl - expand disk->part_tbl
852*540eed56STejun Heo  * @disk: disk to expand part_tbl for
853*540eed56STejun Heo  * @partno: expand such that this partno can fit in
854*540eed56STejun Heo  *
855*540eed56STejun Heo  * Expand disk->part_tbl such that @partno can fit in.  disk->part_tbl
856*540eed56STejun Heo  * uses RCU to allow unlocked dereferencing for stats and other stuff.
857*540eed56STejun Heo  *
858*540eed56STejun Heo  * LOCKING:
859*540eed56STejun Heo  * Matching bd_mutex locked, might sleep.
860*540eed56STejun Heo  *
861*540eed56STejun Heo  * RETURNS:
862*540eed56STejun Heo  * 0 on success, -errno on failure.
863*540eed56STejun Heo  */
864*540eed56STejun Heo int disk_expand_part_tbl(struct gendisk *disk, int partno)
865*540eed56STejun Heo {
866*540eed56STejun Heo 	struct disk_part_tbl *old_ptbl = disk->part_tbl;
867*540eed56STejun Heo 	struct disk_part_tbl *new_ptbl;
868*540eed56STejun Heo 	int len = old_ptbl ? old_ptbl->len : 0;
869*540eed56STejun Heo 	int target = partno + 1;
870*540eed56STejun Heo 	size_t size;
871*540eed56STejun Heo 	int i;
872*540eed56STejun Heo 
873*540eed56STejun Heo 	/* disk_max_parts() is zero during initialization, ignore if so */
874*540eed56STejun Heo 	if (disk_max_parts(disk) && target > disk_max_parts(disk))
875*540eed56STejun Heo 		return -EINVAL;
876*540eed56STejun Heo 
877*540eed56STejun Heo 	if (target <= len)
878*540eed56STejun Heo 		return 0;
879*540eed56STejun Heo 
880*540eed56STejun Heo 	size = sizeof(*new_ptbl) + target * sizeof(new_ptbl->part[0]);
881*540eed56STejun Heo 	new_ptbl = kzalloc_node(size, GFP_KERNEL, disk->node_id);
882*540eed56STejun Heo 	if (!new_ptbl)
883*540eed56STejun Heo 		return -ENOMEM;
884*540eed56STejun Heo 
885*540eed56STejun Heo 	INIT_RCU_HEAD(&new_ptbl->rcu_head);
886*540eed56STejun Heo 	new_ptbl->len = target;
887*540eed56STejun Heo 
888*540eed56STejun Heo 	for (i = 0; i < len; i++)
889*540eed56STejun Heo 		rcu_assign_pointer(new_ptbl->part[i], old_ptbl->part[i]);
890*540eed56STejun Heo 
891*540eed56STejun Heo 	disk_replace_part_tbl(disk, new_ptbl);
892*540eed56STejun Heo 	return 0;
893*540eed56STejun Heo }
894*540eed56STejun Heo 
895edfaa7c3SKay Sievers static void disk_release(struct device *dev)
8961da177e4SLinus Torvalds {
897edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
898edfaa7c3SKay Sievers 
8991da177e4SLinus Torvalds 	kfree(disk->random);
900*540eed56STejun Heo 	disk_replace_part_tbl(disk, NULL);
901074a7acaSTejun Heo 	free_part_stats(&disk->part0);
9021da177e4SLinus Torvalds 	kfree(disk);
9031da177e4SLinus Torvalds }
904edfaa7c3SKay Sievers struct class block_class = {
905edfaa7c3SKay Sievers 	.name		= "block",
9061da177e4SLinus Torvalds };
9071da177e4SLinus Torvalds 
9081826eadfSAdrian Bunk static struct device_type disk_type = {
909edfaa7c3SKay Sievers 	.name		= "disk",
910edfaa7c3SKay Sievers 	.groups		= disk_attr_groups,
911edfaa7c3SKay Sievers 	.release	= disk_release,
9121da177e4SLinus Torvalds };
9131da177e4SLinus Torvalds 
914a6e2ba88SRandy Dunlap #ifdef CONFIG_PROC_FS
915cf771cb5STejun Heo /*
916cf771cb5STejun Heo  * aggregate disk stat collector.  Uses the same stats that the sysfs
917cf771cb5STejun Heo  * entries do, above, but makes them available through one seq_file.
918cf771cb5STejun Heo  *
919cf771cb5STejun Heo  * The output looks suspiciously like /proc/partitions with a bunch of
920cf771cb5STejun Heo  * extra fields.
921cf771cb5STejun Heo  */
922cf771cb5STejun Heo static int diskstats_show(struct seq_file *seqf, void *v)
9231da177e4SLinus Torvalds {
9241da177e4SLinus Torvalds 	struct gendisk *gp = v;
925e71bf0d0STejun Heo 	struct disk_part_iter piter;
926e71bf0d0STejun Heo 	struct hd_struct *hd;
9271da177e4SLinus Torvalds 	char buf[BDEVNAME_SIZE];
928c9959059STejun Heo 	int cpu;
9291da177e4SLinus Torvalds 
9301da177e4SLinus Torvalds 	/*
931ed9e1982STejun Heo 	if (&disk_to_dev(gp)->kobj.entry == block_class.devices.next)
932cf771cb5STejun Heo 		seq_puts(seqf,	"major minor name"
9331da177e4SLinus Torvalds 				"     rio rmerge rsect ruse wio wmerge "
9341da177e4SLinus Torvalds 				"wsect wuse running use aveq"
9351da177e4SLinus Torvalds 				"\n\n");
9361da177e4SLinus Torvalds 	*/
9371da177e4SLinus Torvalds 
938074a7acaSTejun Heo 	disk_part_iter_init(&piter, gp, DISK_PITER_INCL_PART0);
939e71bf0d0STejun Heo 	while ((hd = disk_part_iter_next(&piter))) {
940074a7acaSTejun Heo 		cpu = part_stat_lock();
941c9959059STejun Heo 		part_round_stats(cpu, hd);
942074a7acaSTejun Heo 		part_stat_unlock();
9431f014290STejun Heo 		seq_printf(seqf, "%4d %7d %s %lu %lu %llu "
94428f39d55SJerome Marchand 			   "%u %lu %lu %llu %u %u %u %u\n",
945f331c029STejun Heo 			   MAJOR(part_devt(hd)), MINOR(part_devt(hd)),
946f331c029STejun Heo 			   disk_name(gp, hd->partno, buf),
94728f39d55SJerome Marchand 			   part_stat_read(hd, ios[0]),
94828f39d55SJerome Marchand 			   part_stat_read(hd, merges[0]),
94928f39d55SJerome Marchand 			   (unsigned long long)part_stat_read(hd, sectors[0]),
95028f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, ticks[0])),
95128f39d55SJerome Marchand 			   part_stat_read(hd, ios[1]),
95228f39d55SJerome Marchand 			   part_stat_read(hd, merges[1]),
95328f39d55SJerome Marchand 			   (unsigned long long)part_stat_read(hd, sectors[1]),
95428f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, ticks[1])),
95528f39d55SJerome Marchand 			   hd->in_flight,
95628f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, io_ticks)),
95728f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, time_in_queue))
95828f39d55SJerome Marchand 			);
9591da177e4SLinus Torvalds 	}
960e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
9611da177e4SLinus Torvalds 
9621da177e4SLinus Torvalds 	return 0;
9631da177e4SLinus Torvalds }
9641da177e4SLinus Torvalds 
96512f32bb3SJan Engelhardt const struct seq_operations diskstats_op = {
966def4e38dSTejun Heo 	.start	= disk_seqf_start,
967def4e38dSTejun Heo 	.next	= disk_seqf_next,
968def4e38dSTejun Heo 	.stop	= disk_seqf_stop,
9691da177e4SLinus Torvalds 	.show	= diskstats_show
9701da177e4SLinus Torvalds };
971a6e2ba88SRandy Dunlap #endif /* CONFIG_PROC_FS */
9721da177e4SLinus Torvalds 
9738ce7ad7bSKristen Carlson Accardi static void media_change_notify_thread(struct work_struct *work)
9748ce7ad7bSKristen Carlson Accardi {
9758ce7ad7bSKristen Carlson Accardi 	struct gendisk *gd = container_of(work, struct gendisk, async_notify);
9768ce7ad7bSKristen Carlson Accardi 	char event[] = "MEDIA_CHANGE=1";
9778ce7ad7bSKristen Carlson Accardi 	char *envp[] = { event, NULL };
9788ce7ad7bSKristen Carlson Accardi 
9798ce7ad7bSKristen Carlson Accardi 	/*
9808ce7ad7bSKristen Carlson Accardi 	 * set enviroment vars to indicate which event this is for
9818ce7ad7bSKristen Carlson Accardi 	 * so that user space will know to go check the media status.
9828ce7ad7bSKristen Carlson Accardi 	 */
983ed9e1982STejun Heo 	kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp);
9848ce7ad7bSKristen Carlson Accardi 	put_device(gd->driverfs_dev);
9858ce7ad7bSKristen Carlson Accardi }
9868ce7ad7bSKristen Carlson Accardi 
9871826eadfSAdrian Bunk #if 0
9888ce7ad7bSKristen Carlson Accardi void genhd_media_change_notify(struct gendisk *disk)
9898ce7ad7bSKristen Carlson Accardi {
9908ce7ad7bSKristen Carlson Accardi 	get_device(disk->driverfs_dev);
9918ce7ad7bSKristen Carlson Accardi 	schedule_work(&disk->async_notify);
9928ce7ad7bSKristen Carlson Accardi }
9938ce7ad7bSKristen Carlson Accardi EXPORT_SYMBOL_GPL(genhd_media_change_notify);
9941826eadfSAdrian Bunk #endif  /*  0  */
9958ce7ad7bSKristen Carlson Accardi 
996cf771cb5STejun Heo dev_t blk_lookup_devt(const char *name, int partno)
997edfaa7c3SKay Sievers {
998edfaa7c3SKay Sievers 	dev_t devt = MKDEV(0, 0);
999def4e38dSTejun Heo 	struct class_dev_iter iter;
1000def4e38dSTejun Heo 	struct device *dev;
1001edfaa7c3SKay Sievers 
1002def4e38dSTejun Heo 	class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
1003def4e38dSTejun Heo 	while ((dev = class_dev_iter_next(&iter))) {
1004def4e38dSTejun Heo 		struct gendisk *disk = dev_to_disk(dev);
1005548b10ebSTejun Heo 		struct hd_struct *part;
1006def4e38dSTejun Heo 
1007f331c029STejun Heo 		if (strcmp(dev->bus_id, name))
1008f331c029STejun Heo 			continue;
1009f331c029STejun Heo 
1010e71bf0d0STejun Heo 		part = disk_get_part(disk, partno);
1011548b10ebSTejun Heo 		if (part && (part->nr_sects || partno == 0)) {
1012f331c029STejun Heo 			devt = part_devt(part);
1013e71bf0d0STejun Heo 			disk_put_part(part);
1014f331c029STejun Heo 			break;
1015def4e38dSTejun Heo 		}
1016548b10ebSTejun Heo 		disk_put_part(part);
1017548b10ebSTejun Heo 	}
1018def4e38dSTejun Heo 	class_dev_iter_exit(&iter);
1019edfaa7c3SKay Sievers 	return devt;
1020edfaa7c3SKay Sievers }
1021edfaa7c3SKay Sievers EXPORT_SYMBOL(blk_lookup_devt);
1022edfaa7c3SKay Sievers 
10231da177e4SLinus Torvalds struct gendisk *alloc_disk(int minors)
10241da177e4SLinus Torvalds {
10251946089aSChristoph Lameter 	return alloc_disk_node(minors, -1);
10261946089aSChristoph Lameter }
10271946089aSChristoph Lameter 
10281946089aSChristoph Lameter struct gendisk *alloc_disk_node(int minors, int node_id)
10291946089aSChristoph Lameter {
1030bcce3de1STejun Heo 	return alloc_disk_ext_node(minors, 0, node_id);
1031bcce3de1STejun Heo }
1032bcce3de1STejun Heo 
1033bcce3de1STejun Heo struct gendisk *alloc_disk_ext(int minors, int ext_minors)
1034bcce3de1STejun Heo {
1035bcce3de1STejun Heo 	return alloc_disk_ext_node(minors, ext_minors, -1);
1036bcce3de1STejun Heo }
1037bcce3de1STejun Heo 
1038bcce3de1STejun Heo struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id)
1039bcce3de1STejun Heo {
10401946089aSChristoph Lameter 	struct gendisk *disk;
10411946089aSChristoph Lameter 
104294f6030cSChristoph Lameter 	disk = kmalloc_node(sizeof(struct gendisk),
104394f6030cSChristoph Lameter 				GFP_KERNEL | __GFP_ZERO, node_id);
10441da177e4SLinus Torvalds 	if (disk) {
1045074a7acaSTejun Heo 		if (!init_part_stats(&disk->part0)) {
10461da177e4SLinus Torvalds 			kfree(disk);
10471da177e4SLinus Torvalds 			return NULL;
10481da177e4SLinus Torvalds 		}
1049*540eed56STejun Heo 		if (disk_expand_part_tbl(disk, 0)) {
1050074a7acaSTejun Heo 			free_part_stats(&disk->part0);
10511da177e4SLinus Torvalds 			kfree(disk);
10521da177e4SLinus Torvalds 			return NULL;
10531da177e4SLinus Torvalds 		}
1054*540eed56STejun Heo 		disk->part_tbl->part[0] = &disk->part0;
1055b5d0b9dfSTejun Heo 
10561da177e4SLinus Torvalds 		disk->minors = minors;
1057bcce3de1STejun Heo 		disk->ext_minors = ext_minors;
10581da177e4SLinus Torvalds 		rand_initialize_disk(disk);
1059ed9e1982STejun Heo 		disk_to_dev(disk)->class = &block_class;
1060ed9e1982STejun Heo 		disk_to_dev(disk)->type = &disk_type;
1061ed9e1982STejun Heo 		device_initialize(disk_to_dev(disk));
10628ce7ad7bSKristen Carlson Accardi 		INIT_WORK(&disk->async_notify,
10638ce7ad7bSKristen Carlson Accardi 			media_change_notify_thread);
1064*540eed56STejun Heo 		disk->node_id = node_id;
10651da177e4SLinus Torvalds 	}
10661da177e4SLinus Torvalds 	return disk;
10671da177e4SLinus Torvalds }
10681da177e4SLinus Torvalds 
10691da177e4SLinus Torvalds EXPORT_SYMBOL(alloc_disk);
10701946089aSChristoph Lameter EXPORT_SYMBOL(alloc_disk_node);
1071bcce3de1STejun Heo EXPORT_SYMBOL(alloc_disk_ext);
1072bcce3de1STejun Heo EXPORT_SYMBOL(alloc_disk_ext_node);
10731da177e4SLinus Torvalds 
10741da177e4SLinus Torvalds struct kobject *get_disk(struct gendisk *disk)
10751da177e4SLinus Torvalds {
10761da177e4SLinus Torvalds 	struct module *owner;
10771da177e4SLinus Torvalds 	struct kobject *kobj;
10781da177e4SLinus Torvalds 
10791da177e4SLinus Torvalds 	if (!disk->fops)
10801da177e4SLinus Torvalds 		return NULL;
10811da177e4SLinus Torvalds 	owner = disk->fops->owner;
10821da177e4SLinus Torvalds 	if (owner && !try_module_get(owner))
10831da177e4SLinus Torvalds 		return NULL;
1084ed9e1982STejun Heo 	kobj = kobject_get(&disk_to_dev(disk)->kobj);
10851da177e4SLinus Torvalds 	if (kobj == NULL) {
10861da177e4SLinus Torvalds 		module_put(owner);
10871da177e4SLinus Torvalds 		return NULL;
10881da177e4SLinus Torvalds 	}
10891da177e4SLinus Torvalds 	return kobj;
10901da177e4SLinus Torvalds 
10911da177e4SLinus Torvalds }
10921da177e4SLinus Torvalds 
10931da177e4SLinus Torvalds EXPORT_SYMBOL(get_disk);
10941da177e4SLinus Torvalds 
10951da177e4SLinus Torvalds void put_disk(struct gendisk *disk)
10961da177e4SLinus Torvalds {
10971da177e4SLinus Torvalds 	if (disk)
1098ed9e1982STejun Heo 		kobject_put(&disk_to_dev(disk)->kobj);
10991da177e4SLinus Torvalds }
11001da177e4SLinus Torvalds 
11011da177e4SLinus Torvalds EXPORT_SYMBOL(put_disk);
11021da177e4SLinus Torvalds 
11031da177e4SLinus Torvalds void set_device_ro(struct block_device *bdev, int flag)
11041da177e4SLinus Torvalds {
11051da177e4SLinus Torvalds 	bdev->bd_part->policy = flag;
11061da177e4SLinus Torvalds }
11071da177e4SLinus Torvalds 
11081da177e4SLinus Torvalds EXPORT_SYMBOL(set_device_ro);
11091da177e4SLinus Torvalds 
11101da177e4SLinus Torvalds void set_disk_ro(struct gendisk *disk, int flag)
11111da177e4SLinus Torvalds {
1112e71bf0d0STejun Heo 	struct disk_part_iter piter;
1113e71bf0d0STejun Heo 	struct hd_struct *part;
1114e71bf0d0STejun Heo 
1115b7db9956STejun Heo 	disk_part_iter_init(&piter, disk,
1116b7db9956STejun Heo 			    DISK_PITER_INCL_EMPTY | DISK_PITER_INCL_PART0);
1117e71bf0d0STejun Heo 	while ((part = disk_part_iter_next(&piter)))
1118e71bf0d0STejun Heo 		part->policy = flag;
1119e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
11201da177e4SLinus Torvalds }
11211da177e4SLinus Torvalds 
11221da177e4SLinus Torvalds EXPORT_SYMBOL(set_disk_ro);
11231da177e4SLinus Torvalds 
11241da177e4SLinus Torvalds int bdev_read_only(struct block_device *bdev)
11251da177e4SLinus Torvalds {
11261da177e4SLinus Torvalds 	if (!bdev)
11271da177e4SLinus Torvalds 		return 0;
11281da177e4SLinus Torvalds 	return bdev->bd_part->policy;
11291da177e4SLinus Torvalds }
11301da177e4SLinus Torvalds 
11311da177e4SLinus Torvalds EXPORT_SYMBOL(bdev_read_only);
11321da177e4SLinus Torvalds 
1133cf771cb5STejun Heo int invalidate_partition(struct gendisk *disk, int partno)
11341da177e4SLinus Torvalds {
11351da177e4SLinus Torvalds 	int res = 0;
1136cf771cb5STejun Heo 	struct block_device *bdev = bdget_disk(disk, partno);
11371da177e4SLinus Torvalds 	if (bdev) {
11382ef41634SChristoph Hellwig 		fsync_bdev(bdev);
11392ef41634SChristoph Hellwig 		res = __invalidate_device(bdev);
11401da177e4SLinus Torvalds 		bdput(bdev);
11411da177e4SLinus Torvalds 	}
11421da177e4SLinus Torvalds 	return res;
11431da177e4SLinus Torvalds }
11441da177e4SLinus Torvalds 
11451da177e4SLinus Torvalds EXPORT_SYMBOL(invalidate_partition);
1146