xref: /linux/block/genhd.c (revision b5af921ec02333e943efb59aca4f56b78fc0e100)
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>
13f500975aSAlexey Dobriyan #include <linux/proc_fs.h>
141da177e4SLinus Torvalds #include <linux/seq_file.h>
151da177e4SLinus Torvalds #include <linux/slab.h>
161da177e4SLinus Torvalds #include <linux/kmod.h>
171da177e4SLinus Torvalds #include <linux/kobj_map.h>
182ef41634SChristoph Hellwig #include <linux/buffer_head.h>
1958383af6SJes Sorensen #include <linux/mutex.h>
20bcce3de1STejun Heo #include <linux/idr.h>
211da177e4SLinus Torvalds 
22ff88972cSAdrian Bunk #include "blk.h"
23ff88972cSAdrian Bunk 
24edfaa7c3SKay Sievers static DEFINE_MUTEX(block_class_lock);
25edfaa7c3SKay Sievers #ifndef CONFIG_SYSFS_DEPRECATED
26edfaa7c3SKay Sievers struct kobject *block_depr;
27edfaa7c3SKay Sievers #endif
281da177e4SLinus Torvalds 
29bcce3de1STejun Heo /* for extended dynamic devt allocation, currently only one major is used */
30bcce3de1STejun Heo #define MAX_EXT_DEVT		(1 << MINORBITS)
31bcce3de1STejun Heo 
32bcce3de1STejun Heo /* For extended devt allocation.  ext_devt_mutex prevents look up
33bcce3de1STejun Heo  * results from going away underneath its user.
34bcce3de1STejun Heo  */
35bcce3de1STejun Heo static DEFINE_MUTEX(ext_devt_mutex);
36bcce3de1STejun Heo static DEFINE_IDR(ext_devt_idr);
37bcce3de1STejun Heo 
381826eadfSAdrian Bunk static struct device_type disk_type;
391826eadfSAdrian Bunk 
40e71bf0d0STejun Heo /**
41e71bf0d0STejun Heo  * disk_get_part - get partition
42e71bf0d0STejun Heo  * @disk: disk to look partition from
43e71bf0d0STejun Heo  * @partno: partition number
44e71bf0d0STejun Heo  *
45e71bf0d0STejun Heo  * Look for partition @partno from @disk.  If found, increment
46e71bf0d0STejun Heo  * reference count and return it.
47e71bf0d0STejun Heo  *
48e71bf0d0STejun Heo  * CONTEXT:
49e71bf0d0STejun Heo  * Don't care.
50e71bf0d0STejun Heo  *
51e71bf0d0STejun Heo  * RETURNS:
52e71bf0d0STejun Heo  * Pointer to the found partition on success, NULL if not found.
53e71bf0d0STejun Heo  */
54e71bf0d0STejun Heo struct hd_struct *disk_get_part(struct gendisk *disk, int partno)
55e71bf0d0STejun Heo {
56540eed56STejun Heo 	struct hd_struct *part = NULL;
57540eed56STejun Heo 	struct disk_part_tbl *ptbl;
58e71bf0d0STejun Heo 
59540eed56STejun Heo 	if (unlikely(partno < 0))
60e71bf0d0STejun Heo 		return NULL;
61540eed56STejun Heo 
62e71bf0d0STejun Heo 	rcu_read_lock();
63540eed56STejun Heo 
64540eed56STejun Heo 	ptbl = rcu_dereference(disk->part_tbl);
65540eed56STejun Heo 	if (likely(partno < ptbl->len)) {
66540eed56STejun Heo 		part = rcu_dereference(ptbl->part[partno]);
67e71bf0d0STejun Heo 		if (part)
68ed9e1982STejun Heo 			get_device(part_to_dev(part));
69540eed56STejun Heo 	}
70540eed56STejun Heo 
71e71bf0d0STejun Heo 	rcu_read_unlock();
72e71bf0d0STejun Heo 
73e71bf0d0STejun Heo 	return part;
74e71bf0d0STejun Heo }
75e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_get_part);
76e71bf0d0STejun Heo 
77e71bf0d0STejun Heo /**
78e71bf0d0STejun Heo  * disk_part_iter_init - initialize partition iterator
79e71bf0d0STejun Heo  * @piter: iterator to initialize
80e71bf0d0STejun Heo  * @disk: disk to iterate over
81e71bf0d0STejun Heo  * @flags: DISK_PITER_* flags
82e71bf0d0STejun Heo  *
83e71bf0d0STejun Heo  * Initialize @piter so that it iterates over partitions of @disk.
84e71bf0d0STejun Heo  *
85e71bf0d0STejun Heo  * CONTEXT:
86e71bf0d0STejun Heo  * Don't care.
87e71bf0d0STejun Heo  */
88e71bf0d0STejun Heo void disk_part_iter_init(struct disk_part_iter *piter, struct gendisk *disk,
89e71bf0d0STejun Heo 			  unsigned int flags)
90e71bf0d0STejun Heo {
91540eed56STejun Heo 	struct disk_part_tbl *ptbl;
92540eed56STejun Heo 
93540eed56STejun Heo 	rcu_read_lock();
94540eed56STejun Heo 	ptbl = rcu_dereference(disk->part_tbl);
95540eed56STejun Heo 
96e71bf0d0STejun Heo 	piter->disk = disk;
97e71bf0d0STejun Heo 	piter->part = NULL;
98e71bf0d0STejun Heo 
99e71bf0d0STejun Heo 	if (flags & DISK_PITER_REVERSE)
100540eed56STejun Heo 		piter->idx = ptbl->len - 1;
10171982a40STejun Heo 	else if (flags & (DISK_PITER_INCL_PART0 | DISK_PITER_INCL_EMPTY_PART0))
102e71bf0d0STejun Heo 		piter->idx = 0;
103b5d0b9dfSTejun Heo 	else
104b5d0b9dfSTejun Heo 		piter->idx = 1;
105e71bf0d0STejun Heo 
106e71bf0d0STejun Heo 	piter->flags = flags;
107540eed56STejun Heo 
108540eed56STejun Heo 	rcu_read_unlock();
109e71bf0d0STejun Heo }
110e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_part_iter_init);
111e71bf0d0STejun Heo 
112e71bf0d0STejun Heo /**
113e71bf0d0STejun Heo  * disk_part_iter_next - proceed iterator to the next partition and return it
114e71bf0d0STejun Heo  * @piter: iterator of interest
115e71bf0d0STejun Heo  *
116e71bf0d0STejun Heo  * Proceed @piter to the next partition and return it.
117e71bf0d0STejun Heo  *
118e71bf0d0STejun Heo  * CONTEXT:
119e71bf0d0STejun Heo  * Don't care.
120e71bf0d0STejun Heo  */
121e71bf0d0STejun Heo struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter)
122e71bf0d0STejun Heo {
123540eed56STejun Heo 	struct disk_part_tbl *ptbl;
124e71bf0d0STejun Heo 	int inc, end;
125e71bf0d0STejun Heo 
126e71bf0d0STejun Heo 	/* put the last partition */
127e71bf0d0STejun Heo 	disk_put_part(piter->part);
128e71bf0d0STejun Heo 	piter->part = NULL;
129e71bf0d0STejun Heo 
130540eed56STejun Heo 	/* get part_tbl */
131e71bf0d0STejun Heo 	rcu_read_lock();
132540eed56STejun Heo 	ptbl = rcu_dereference(piter->disk->part_tbl);
133e71bf0d0STejun Heo 
134e71bf0d0STejun Heo 	/* determine iteration parameters */
135e71bf0d0STejun Heo 	if (piter->flags & DISK_PITER_REVERSE) {
136e71bf0d0STejun Heo 		inc = -1;
13771982a40STejun Heo 		if (piter->flags & (DISK_PITER_INCL_PART0 |
13871982a40STejun Heo 				    DISK_PITER_INCL_EMPTY_PART0))
139e71bf0d0STejun Heo 			end = -1;
140b5d0b9dfSTejun Heo 		else
141b5d0b9dfSTejun Heo 			end = 0;
142e71bf0d0STejun Heo 	} else {
143e71bf0d0STejun Heo 		inc = 1;
144540eed56STejun Heo 		end = ptbl->len;
145e71bf0d0STejun Heo 	}
146e71bf0d0STejun Heo 
147e71bf0d0STejun Heo 	/* iterate to the next partition */
148e71bf0d0STejun Heo 	for (; piter->idx != end; piter->idx += inc) {
149e71bf0d0STejun Heo 		struct hd_struct *part;
150e71bf0d0STejun Heo 
151540eed56STejun Heo 		part = rcu_dereference(ptbl->part[piter->idx]);
152e71bf0d0STejun Heo 		if (!part)
153e71bf0d0STejun Heo 			continue;
15471982a40STejun Heo 		if (!part->nr_sects &&
15571982a40STejun Heo 		    !(piter->flags & DISK_PITER_INCL_EMPTY) &&
15671982a40STejun Heo 		    !(piter->flags & DISK_PITER_INCL_EMPTY_PART0 &&
15771982a40STejun Heo 		      piter->idx == 0))
158e71bf0d0STejun Heo 			continue;
159e71bf0d0STejun Heo 
160ed9e1982STejun Heo 		get_device(part_to_dev(part));
161e71bf0d0STejun Heo 		piter->part = part;
162e71bf0d0STejun Heo 		piter->idx += inc;
163e71bf0d0STejun Heo 		break;
164e71bf0d0STejun Heo 	}
165e71bf0d0STejun Heo 
166e71bf0d0STejun Heo 	rcu_read_unlock();
167e71bf0d0STejun Heo 
168e71bf0d0STejun Heo 	return piter->part;
169e71bf0d0STejun Heo }
170e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_part_iter_next);
171e71bf0d0STejun Heo 
172e71bf0d0STejun Heo /**
173e71bf0d0STejun Heo  * disk_part_iter_exit - finish up partition iteration
174e71bf0d0STejun Heo  * @piter: iter of interest
175e71bf0d0STejun Heo  *
176e71bf0d0STejun Heo  * Called when iteration is over.  Cleans up @piter.
177e71bf0d0STejun Heo  *
178e71bf0d0STejun Heo  * CONTEXT:
179e71bf0d0STejun Heo  * Don't care.
180e71bf0d0STejun Heo  */
181e71bf0d0STejun Heo void disk_part_iter_exit(struct disk_part_iter *piter)
182e71bf0d0STejun Heo {
183e71bf0d0STejun Heo 	disk_put_part(piter->part);
184e71bf0d0STejun Heo 	piter->part = NULL;
185e71bf0d0STejun Heo }
186e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_part_iter_exit);
187e71bf0d0STejun Heo 
188a6f23657SJens Axboe static inline int sector_in_part(struct hd_struct *part, sector_t sector)
189a6f23657SJens Axboe {
190a6f23657SJens Axboe 	return part->start_sect <= sector &&
191a6f23657SJens Axboe 		sector < part->start_sect + part->nr_sects;
192a6f23657SJens Axboe }
193a6f23657SJens Axboe 
194e71bf0d0STejun Heo /**
195e71bf0d0STejun Heo  * disk_map_sector_rcu - map sector to partition
196e71bf0d0STejun Heo  * @disk: gendisk of interest
197e71bf0d0STejun Heo  * @sector: sector to map
198e71bf0d0STejun Heo  *
199e71bf0d0STejun Heo  * Find out which partition @sector maps to on @disk.  This is
200e71bf0d0STejun Heo  * primarily used for stats accounting.
201e71bf0d0STejun Heo  *
202e71bf0d0STejun Heo  * CONTEXT:
203e71bf0d0STejun Heo  * RCU read locked.  The returned partition pointer is valid only
204e71bf0d0STejun Heo  * while preemption is disabled.
205e71bf0d0STejun Heo  *
206e71bf0d0STejun Heo  * RETURNS:
207074a7acaSTejun Heo  * Found partition on success, part0 is returned if no partition matches
208e71bf0d0STejun Heo  */
209e71bf0d0STejun Heo struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector)
210e71bf0d0STejun Heo {
211540eed56STejun Heo 	struct disk_part_tbl *ptbl;
212a6f23657SJens Axboe 	struct hd_struct *part;
213e71bf0d0STejun Heo 	int i;
214e71bf0d0STejun Heo 
215540eed56STejun Heo 	ptbl = rcu_dereference(disk->part_tbl);
216540eed56STejun Heo 
217a6f23657SJens Axboe 	part = rcu_dereference(ptbl->last_lookup);
218a6f23657SJens Axboe 	if (part && sector_in_part(part, sector))
219e71bf0d0STejun Heo 		return part;
220a6f23657SJens Axboe 
221a6f23657SJens Axboe 	for (i = 1; i < ptbl->len; i++) {
222a6f23657SJens Axboe 		part = rcu_dereference(ptbl->part[i]);
223a6f23657SJens Axboe 
224a6f23657SJens Axboe 		if (part && sector_in_part(part, sector)) {
225a6f23657SJens Axboe 			rcu_assign_pointer(ptbl->last_lookup, part);
226a6f23657SJens Axboe 			return part;
227a6f23657SJens Axboe 		}
228e71bf0d0STejun Heo 	}
229074a7acaSTejun Heo 	return &disk->part0;
230e71bf0d0STejun Heo }
231e71bf0d0STejun Heo EXPORT_SYMBOL_GPL(disk_map_sector_rcu);
232e71bf0d0STejun Heo 
2331da177e4SLinus Torvalds /*
2341da177e4SLinus Torvalds  * Can be deleted altogether. Later.
2351da177e4SLinus Torvalds  *
2361da177e4SLinus Torvalds  */
2371da177e4SLinus Torvalds static struct blk_major_name {
2381da177e4SLinus Torvalds 	struct blk_major_name *next;
2391da177e4SLinus Torvalds 	int major;
2401da177e4SLinus Torvalds 	char name[16];
24168eef3b4SJoe Korty } *major_names[BLKDEV_MAJOR_HASH_SIZE];
2421da177e4SLinus Torvalds 
2431da177e4SLinus Torvalds /* index in the above - for now: assume no multimajor ranges */
2441da177e4SLinus Torvalds static inline int major_to_index(int major)
2451da177e4SLinus Torvalds {
24668eef3b4SJoe Korty 	return major % BLKDEV_MAJOR_HASH_SIZE;
2471da177e4SLinus Torvalds }
2481da177e4SLinus Torvalds 
24968eef3b4SJoe Korty #ifdef CONFIG_PROC_FS
250cf771cb5STejun Heo void blkdev_show(struct seq_file *seqf, off_t offset)
2517170be5fSNeil Horman {
25268eef3b4SJoe Korty 	struct blk_major_name *dp;
2537170be5fSNeil Horman 
25468eef3b4SJoe Korty 	if (offset < BLKDEV_MAJOR_HASH_SIZE) {
255edfaa7c3SKay Sievers 		mutex_lock(&block_class_lock);
25668eef3b4SJoe Korty 		for (dp = major_names[offset]; dp; dp = dp->next)
257cf771cb5STejun Heo 			seq_printf(seqf, "%3d %s\n", dp->major, dp->name);
258edfaa7c3SKay Sievers 		mutex_unlock(&block_class_lock);
25968eef3b4SJoe Korty 	}
2607170be5fSNeil Horman }
26168eef3b4SJoe Korty #endif /* CONFIG_PROC_FS */
2621da177e4SLinus Torvalds 
2639e8c0bccSMárton Németh /**
2649e8c0bccSMárton Németh  * register_blkdev - register a new block device
2659e8c0bccSMárton Németh  *
2669e8c0bccSMárton Németh  * @major: the requested major device number [1..255]. If @major=0, try to
2679e8c0bccSMárton Németh  *         allocate any unused major number.
2689e8c0bccSMárton Németh  * @name: the name of the new block device as a zero terminated string
2699e8c0bccSMárton Németh  *
2709e8c0bccSMárton Németh  * The @name must be unique within the system.
2719e8c0bccSMárton Németh  *
2729e8c0bccSMárton Németh  * The return value depends on the @major input parameter.
2739e8c0bccSMárton Németh  *  - if a major device number was requested in range [1..255] then the
2749e8c0bccSMárton Németh  *    function returns zero on success, or a negative error code
2759e8c0bccSMárton Németh  *  - if any unused major number was requested with @major=0 parameter
2769e8c0bccSMárton Németh  *    then the return value is the allocated major number in range
2779e8c0bccSMárton Németh  *    [1..255] or a negative error code otherwise
2789e8c0bccSMárton Németh  */
2791da177e4SLinus Torvalds int register_blkdev(unsigned int major, const char *name)
2801da177e4SLinus Torvalds {
2811da177e4SLinus Torvalds 	struct blk_major_name **n, *p;
2821da177e4SLinus Torvalds 	int index, ret = 0;
2831da177e4SLinus Torvalds 
284edfaa7c3SKay Sievers 	mutex_lock(&block_class_lock);
2851da177e4SLinus Torvalds 
2861da177e4SLinus Torvalds 	/* temporary */
2871da177e4SLinus Torvalds 	if (major == 0) {
2881da177e4SLinus Torvalds 		for (index = ARRAY_SIZE(major_names)-1; index > 0; index--) {
2891da177e4SLinus Torvalds 			if (major_names[index] == NULL)
2901da177e4SLinus Torvalds 				break;
2911da177e4SLinus Torvalds 		}
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 		if (index == 0) {
2941da177e4SLinus Torvalds 			printk("register_blkdev: failed to get major for %s\n",
2951da177e4SLinus Torvalds 			       name);
2961da177e4SLinus Torvalds 			ret = -EBUSY;
2971da177e4SLinus Torvalds 			goto out;
2981da177e4SLinus Torvalds 		}
2991da177e4SLinus Torvalds 		major = index;
3001da177e4SLinus Torvalds 		ret = major;
3011da177e4SLinus Torvalds 	}
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds 	p = kmalloc(sizeof(struct blk_major_name), GFP_KERNEL);
3041da177e4SLinus Torvalds 	if (p == NULL) {
3051da177e4SLinus Torvalds 		ret = -ENOMEM;
3061da177e4SLinus Torvalds 		goto out;
3071da177e4SLinus Torvalds 	}
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds 	p->major = major;
3101da177e4SLinus Torvalds 	strlcpy(p->name, name, sizeof(p->name));
3111da177e4SLinus Torvalds 	p->next = NULL;
3121da177e4SLinus Torvalds 	index = major_to_index(major);
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds 	for (n = &major_names[index]; *n; n = &(*n)->next) {
3151da177e4SLinus Torvalds 		if ((*n)->major == major)
3161da177e4SLinus Torvalds 			break;
3171da177e4SLinus Torvalds 	}
3181da177e4SLinus Torvalds 	if (!*n)
3191da177e4SLinus Torvalds 		*n = p;
3201da177e4SLinus Torvalds 	else
3211da177e4SLinus Torvalds 		ret = -EBUSY;
3221da177e4SLinus Torvalds 
3231da177e4SLinus Torvalds 	if (ret < 0) {
3241da177e4SLinus Torvalds 		printk("register_blkdev: cannot get major %d for %s\n",
3251da177e4SLinus Torvalds 		       major, name);
3261da177e4SLinus Torvalds 		kfree(p);
3271da177e4SLinus Torvalds 	}
3281da177e4SLinus Torvalds out:
329edfaa7c3SKay Sievers 	mutex_unlock(&block_class_lock);
3301da177e4SLinus Torvalds 	return ret;
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds EXPORT_SYMBOL(register_blkdev);
3341da177e4SLinus Torvalds 
335f4480240SAkinobu Mita void unregister_blkdev(unsigned int major, const char *name)
3361da177e4SLinus Torvalds {
3371da177e4SLinus Torvalds 	struct blk_major_name **n;
3381da177e4SLinus Torvalds 	struct blk_major_name *p = NULL;
3391da177e4SLinus Torvalds 	int index = major_to_index(major);
3401da177e4SLinus Torvalds 
341edfaa7c3SKay Sievers 	mutex_lock(&block_class_lock);
3421da177e4SLinus Torvalds 	for (n = &major_names[index]; *n; n = &(*n)->next)
3431da177e4SLinus Torvalds 		if ((*n)->major == major)
3441da177e4SLinus Torvalds 			break;
345294462a5SAkinobu Mita 	if (!*n || strcmp((*n)->name, name)) {
346294462a5SAkinobu Mita 		WARN_ON(1);
347294462a5SAkinobu Mita 	} else {
3481da177e4SLinus Torvalds 		p = *n;
3491da177e4SLinus Torvalds 		*n = p->next;
3501da177e4SLinus Torvalds 	}
351edfaa7c3SKay Sievers 	mutex_unlock(&block_class_lock);
3521da177e4SLinus Torvalds 	kfree(p);
3531da177e4SLinus Torvalds }
3541da177e4SLinus Torvalds 
3551da177e4SLinus Torvalds EXPORT_SYMBOL(unregister_blkdev);
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds static struct kobj_map *bdev_map;
3581da177e4SLinus Torvalds 
359bcce3de1STejun Heo /**
360870d6656STejun Heo  * blk_mangle_minor - scatter minor numbers apart
361870d6656STejun Heo  * @minor: minor number to mangle
362870d6656STejun Heo  *
363870d6656STejun Heo  * Scatter consecutively allocated @minor number apart if MANGLE_DEVT
364870d6656STejun Heo  * is enabled.  Mangling twice gives the original value.
365870d6656STejun Heo  *
366870d6656STejun Heo  * RETURNS:
367870d6656STejun Heo  * Mangled value.
368870d6656STejun Heo  *
369870d6656STejun Heo  * CONTEXT:
370870d6656STejun Heo  * Don't care.
371870d6656STejun Heo  */
372870d6656STejun Heo static int blk_mangle_minor(int minor)
373870d6656STejun Heo {
374870d6656STejun Heo #ifdef CONFIG_DEBUG_BLOCK_EXT_DEVT
375870d6656STejun Heo 	int i;
376870d6656STejun Heo 
377870d6656STejun Heo 	for (i = 0; i < MINORBITS / 2; i++) {
378870d6656STejun Heo 		int low = minor & (1 << i);
379870d6656STejun Heo 		int high = minor & (1 << (MINORBITS - 1 - i));
380870d6656STejun Heo 		int distance = MINORBITS - 1 - 2 * i;
381870d6656STejun Heo 
382870d6656STejun Heo 		minor ^= low | high;	/* clear both bits */
383870d6656STejun Heo 		low <<= distance;	/* swap the positions */
384870d6656STejun Heo 		high >>= distance;
385870d6656STejun Heo 		minor |= low | high;	/* and set */
386870d6656STejun Heo 	}
387870d6656STejun Heo #endif
388870d6656STejun Heo 	return minor;
389870d6656STejun Heo }
390870d6656STejun Heo 
391870d6656STejun Heo /**
392bcce3de1STejun Heo  * blk_alloc_devt - allocate a dev_t for a partition
393bcce3de1STejun Heo  * @part: partition to allocate dev_t for
394bcce3de1STejun Heo  * @devt: out parameter for resulting dev_t
395bcce3de1STejun Heo  *
396bcce3de1STejun Heo  * Allocate a dev_t for block device.
397bcce3de1STejun Heo  *
398bcce3de1STejun Heo  * RETURNS:
399bcce3de1STejun Heo  * 0 on success, allocated dev_t is returned in *@devt.  -errno on
400bcce3de1STejun Heo  * failure.
401bcce3de1STejun Heo  *
402bcce3de1STejun Heo  * CONTEXT:
403bcce3de1STejun Heo  * Might sleep.
404bcce3de1STejun Heo  */
405bcce3de1STejun Heo int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
406bcce3de1STejun Heo {
407bcce3de1STejun Heo 	struct gendisk *disk = part_to_disk(part);
408bcce3de1STejun Heo 	int idx, rc;
409bcce3de1STejun Heo 
410bcce3de1STejun Heo 	/* in consecutive minor range? */
411bcce3de1STejun Heo 	if (part->partno < disk->minors) {
412bcce3de1STejun Heo 		*devt = MKDEV(disk->major, disk->first_minor + part->partno);
413bcce3de1STejun Heo 		return 0;
414bcce3de1STejun Heo 	}
415bcce3de1STejun Heo 
416bcce3de1STejun Heo 	/* allocate ext devt */
417bcce3de1STejun Heo 	do {
418bcce3de1STejun Heo 		if (!idr_pre_get(&ext_devt_idr, GFP_KERNEL))
419bcce3de1STejun Heo 			return -ENOMEM;
420bcce3de1STejun Heo 		rc = idr_get_new(&ext_devt_idr, part, &idx);
421bcce3de1STejun Heo 	} while (rc == -EAGAIN);
422bcce3de1STejun Heo 
423bcce3de1STejun Heo 	if (rc)
424bcce3de1STejun Heo 		return rc;
425bcce3de1STejun Heo 
426bcce3de1STejun Heo 	if (idx > MAX_EXT_DEVT) {
427bcce3de1STejun Heo 		idr_remove(&ext_devt_idr, idx);
428bcce3de1STejun Heo 		return -EBUSY;
429bcce3de1STejun Heo 	}
430bcce3de1STejun Heo 
431870d6656STejun Heo 	*devt = MKDEV(BLOCK_EXT_MAJOR, blk_mangle_minor(idx));
432bcce3de1STejun Heo 	return 0;
433bcce3de1STejun Heo }
434bcce3de1STejun Heo 
435bcce3de1STejun Heo /**
436bcce3de1STejun Heo  * blk_free_devt - free a dev_t
437bcce3de1STejun Heo  * @devt: dev_t to free
438bcce3de1STejun Heo  *
439bcce3de1STejun Heo  * Free @devt which was allocated using blk_alloc_devt().
440bcce3de1STejun Heo  *
441bcce3de1STejun Heo  * CONTEXT:
442bcce3de1STejun Heo  * Might sleep.
443bcce3de1STejun Heo  */
444bcce3de1STejun Heo void blk_free_devt(dev_t devt)
445bcce3de1STejun Heo {
446bcce3de1STejun Heo 	might_sleep();
447bcce3de1STejun Heo 
448bcce3de1STejun Heo 	if (devt == MKDEV(0, 0))
449bcce3de1STejun Heo 		return;
450bcce3de1STejun Heo 
451bcce3de1STejun Heo 	if (MAJOR(devt) == BLOCK_EXT_MAJOR) {
452bcce3de1STejun Heo 		mutex_lock(&ext_devt_mutex);
453870d6656STejun Heo 		idr_remove(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
454bcce3de1STejun Heo 		mutex_unlock(&ext_devt_mutex);
455bcce3de1STejun Heo 	}
456bcce3de1STejun Heo }
457bcce3de1STejun Heo 
4581f014290STejun Heo static char *bdevt_str(dev_t devt, char *buf)
4591f014290STejun Heo {
4601f014290STejun Heo 	if (MAJOR(devt) <= 0xff && MINOR(devt) <= 0xff) {
4611f014290STejun Heo 		char tbuf[BDEVT_SIZE];
4621f014290STejun Heo 		snprintf(tbuf, BDEVT_SIZE, "%02x%02x", MAJOR(devt), MINOR(devt));
4631f014290STejun Heo 		snprintf(buf, BDEVT_SIZE, "%-9s", tbuf);
4641f014290STejun Heo 	} else
4651f014290STejun Heo 		snprintf(buf, BDEVT_SIZE, "%03x:%05x", MAJOR(devt), MINOR(devt));
4661f014290STejun Heo 
4671f014290STejun Heo 	return buf;
4681f014290STejun Heo }
4691f014290STejun Heo 
4701da177e4SLinus Torvalds /*
4711da177e4SLinus Torvalds  * Register device numbers dev..(dev+range-1)
4721da177e4SLinus Torvalds  * range must be nonzero
4731da177e4SLinus Torvalds  * The hash chain is sorted on range, so that subranges can override.
4741da177e4SLinus Torvalds  */
475edfaa7c3SKay Sievers void blk_register_region(dev_t devt, unsigned long range, struct module *module,
4761da177e4SLinus Torvalds 			 struct kobject *(*probe)(dev_t, int *, void *),
4771da177e4SLinus Torvalds 			 int (*lock)(dev_t, void *), void *data)
4781da177e4SLinus Torvalds {
479edfaa7c3SKay Sievers 	kobj_map(bdev_map, devt, range, module, probe, lock, data);
4801da177e4SLinus Torvalds }
4811da177e4SLinus Torvalds 
4821da177e4SLinus Torvalds EXPORT_SYMBOL(blk_register_region);
4831da177e4SLinus Torvalds 
484edfaa7c3SKay Sievers void blk_unregister_region(dev_t devt, unsigned long range)
4851da177e4SLinus Torvalds {
486edfaa7c3SKay Sievers 	kobj_unmap(bdev_map, devt, range);
4871da177e4SLinus Torvalds }
4881da177e4SLinus Torvalds 
4891da177e4SLinus Torvalds EXPORT_SYMBOL(blk_unregister_region);
4901da177e4SLinus Torvalds 
491cf771cb5STejun Heo static struct kobject *exact_match(dev_t devt, int *partno, void *data)
4921da177e4SLinus Torvalds {
4931da177e4SLinus Torvalds 	struct gendisk *p = data;
494edfaa7c3SKay Sievers 
495ed9e1982STejun Heo 	return &disk_to_dev(p)->kobj;
4961da177e4SLinus Torvalds }
4971da177e4SLinus Torvalds 
498edfaa7c3SKay Sievers static int exact_lock(dev_t devt, void *data)
4991da177e4SLinus Torvalds {
5001da177e4SLinus Torvalds 	struct gendisk *p = data;
5011da177e4SLinus Torvalds 
5021da177e4SLinus Torvalds 	if (!get_disk(p))
5031da177e4SLinus Torvalds 		return -1;
5041da177e4SLinus Torvalds 	return 0;
5051da177e4SLinus Torvalds }
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds /**
5081da177e4SLinus Torvalds  * add_disk - add partitioning information to kernel list
5091da177e4SLinus Torvalds  * @disk: per-device partitioning information
5101da177e4SLinus Torvalds  *
5111da177e4SLinus Torvalds  * This function registers the partitioning information in @disk
5121da177e4SLinus Torvalds  * with the kernel.
5133e1a7ff8STejun Heo  *
5143e1a7ff8STejun Heo  * FIXME: error handling
5151da177e4SLinus Torvalds  */
5161da177e4SLinus Torvalds void add_disk(struct gendisk *disk)
5171da177e4SLinus Torvalds {
518cf0ca9feSPeter Zijlstra 	struct backing_dev_info *bdi;
5193e1a7ff8STejun Heo 	dev_t devt;
5206ffeea77SGreg Kroah-Hartman 	int retval;
521cf0ca9feSPeter Zijlstra 
5223e1a7ff8STejun Heo 	/* minors == 0 indicates to use ext devt from part0 and should
5233e1a7ff8STejun Heo 	 * be accompanied with EXT_DEVT flag.  Make sure all
5243e1a7ff8STejun Heo 	 * parameters make sense.
5253e1a7ff8STejun Heo 	 */
5263e1a7ff8STejun Heo 	WARN_ON(disk->minors && !(disk->major || disk->first_minor));
5273e1a7ff8STejun Heo 	WARN_ON(!disk->minors && !(disk->flags & GENHD_FL_EXT_DEVT));
5283e1a7ff8STejun Heo 
5291da177e4SLinus Torvalds 	disk->flags |= GENHD_FL_UP;
5303e1a7ff8STejun Heo 
5313e1a7ff8STejun Heo 	retval = blk_alloc_devt(&disk->part0, &devt);
5323e1a7ff8STejun Heo 	if (retval) {
5333e1a7ff8STejun Heo 		WARN_ON(1);
5343e1a7ff8STejun Heo 		return;
5353e1a7ff8STejun Heo 	}
5363e1a7ff8STejun Heo 	disk_to_dev(disk)->devt = devt;
5373e1a7ff8STejun Heo 
5383e1a7ff8STejun Heo 	/* ->major and ->first_minor aren't supposed to be
5393e1a7ff8STejun Heo 	 * dereferenced from here on, but set them just in case.
5403e1a7ff8STejun Heo 	 */
5413e1a7ff8STejun Heo 	disk->major = MAJOR(devt);
5423e1a7ff8STejun Heo 	disk->first_minor = MINOR(devt);
5433e1a7ff8STejun Heo 
544f331c029STejun Heo 	blk_register_region(disk_devt(disk), disk->minors, NULL,
545f331c029STejun Heo 			    exact_match, exact_lock, disk);
5461da177e4SLinus Torvalds 	register_disk(disk);
5471da177e4SLinus Torvalds 	blk_register_queue(disk);
548cf0ca9feSPeter Zijlstra 
549cf0ca9feSPeter Zijlstra 	bdi = &disk->queue->backing_dev_info;
550f331c029STejun Heo 	bdi_register_dev(bdi, disk_devt(disk));
551ed9e1982STejun Heo 	retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj,
552ed9e1982STejun Heo 				   "bdi");
5536ffeea77SGreg Kroah-Hartman 	WARN_ON(retval);
5541da177e4SLinus Torvalds }
5551da177e4SLinus Torvalds 
5561da177e4SLinus Torvalds EXPORT_SYMBOL(add_disk);
5571da177e4SLinus Torvalds EXPORT_SYMBOL(del_gendisk);	/* in partitions/check.c */
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds void unlink_gendisk(struct gendisk *disk)
5601da177e4SLinus Torvalds {
561ed9e1982STejun Heo 	sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi");
562cf0ca9feSPeter Zijlstra 	bdi_unregister(&disk->queue->backing_dev_info);
5631da177e4SLinus Torvalds 	blk_unregister_queue(disk);
564f331c029STejun Heo 	blk_unregister_region(disk_devt(disk), disk->minors);
5651da177e4SLinus Torvalds }
5661da177e4SLinus Torvalds 
5671da177e4SLinus Torvalds /**
5681da177e4SLinus Torvalds  * get_gendisk - get partitioning information for a given device
569710027a4SRandy Dunlap  * @devt: device to get partitioning information for
570496aa8a9SRandy Dunlap  * @partno: returned partition index
5711da177e4SLinus Torvalds  *
5721da177e4SLinus Torvalds  * This function gets the structure containing partitioning
573710027a4SRandy Dunlap  * information for the given device @devt.
5741da177e4SLinus Torvalds  */
575cf771cb5STejun Heo struct gendisk *get_gendisk(dev_t devt, int *partno)
5761da177e4SLinus Torvalds {
577bcce3de1STejun Heo 	struct gendisk *disk = NULL;
578edfaa7c3SKay Sievers 
579bcce3de1STejun Heo 	if (MAJOR(devt) != BLOCK_EXT_MAJOR) {
580bcce3de1STejun Heo 		struct kobject *kobj;
581bcce3de1STejun Heo 
582bcce3de1STejun Heo 		kobj = kobj_lookup(bdev_map, devt, partno);
583bcce3de1STejun Heo 		if (kobj)
584bcce3de1STejun Heo 			disk = dev_to_disk(kobj_to_dev(kobj));
585bcce3de1STejun Heo 	} else {
586bcce3de1STejun Heo 		struct hd_struct *part;
587bcce3de1STejun Heo 
588bcce3de1STejun Heo 		mutex_lock(&ext_devt_mutex);
589870d6656STejun Heo 		part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
590bcce3de1STejun Heo 		if (part && get_disk(part_to_disk(part))) {
591bcce3de1STejun Heo 			*partno = part->partno;
592bcce3de1STejun Heo 			disk = part_to_disk(part);
593bcce3de1STejun Heo 		}
594bcce3de1STejun Heo 		mutex_unlock(&ext_devt_mutex);
595bcce3de1STejun Heo 	}
596bcce3de1STejun Heo 
597bcce3de1STejun Heo 	return disk;
5981da177e4SLinus Torvalds }
599b6ac23afSDivyesh Shah EXPORT_SYMBOL(get_gendisk);
6001da177e4SLinus Torvalds 
601f331c029STejun Heo /**
602f331c029STejun Heo  * bdget_disk - do bdget() by gendisk and partition number
603f331c029STejun Heo  * @disk: gendisk of interest
604f331c029STejun Heo  * @partno: partition number
605f331c029STejun Heo  *
606f331c029STejun Heo  * Find partition @partno from @disk, do bdget() on it.
607f331c029STejun Heo  *
608f331c029STejun Heo  * CONTEXT:
609f331c029STejun Heo  * Don't care.
610f331c029STejun Heo  *
611f331c029STejun Heo  * RETURNS:
612f331c029STejun Heo  * Resulting block_device on success, NULL on failure.
613f331c029STejun Heo  */
614aeb3d3a8SHarvey Harrison struct block_device *bdget_disk(struct gendisk *disk, int partno)
615f331c029STejun Heo {
616e71bf0d0STejun Heo 	struct hd_struct *part;
617548b10ebSTejun Heo 	struct block_device *bdev = NULL;
618f331c029STejun Heo 
619e71bf0d0STejun Heo 	part = disk_get_part(disk, partno);
6202bbedcb4STejun Heo 	if (part)
621548b10ebSTejun Heo 		bdev = bdget(part_devt(part));
622e71bf0d0STejun Heo 	disk_put_part(part);
623f331c029STejun Heo 
624548b10ebSTejun Heo 	return bdev;
625f331c029STejun Heo }
626f331c029STejun Heo EXPORT_SYMBOL(bdget_disk);
627f331c029STejun Heo 
628dd2a345fSDave Gilbert /*
6295c6f35c5SGreg Kroah-Hartman  * print a full list of all partitions - intended for places where the root
6305c6f35c5SGreg Kroah-Hartman  * filesystem can't be mounted and thus to give the victim some idea of what
6315c6f35c5SGreg Kroah-Hartman  * went wrong
6325c6f35c5SGreg Kroah-Hartman  */
6335c6f35c5SGreg Kroah-Hartman void __init printk_all_partitions(void)
6345c6f35c5SGreg Kroah-Hartman {
635def4e38dSTejun Heo 	struct class_dev_iter iter;
636def4e38dSTejun Heo 	struct device *dev;
637def4e38dSTejun Heo 
638def4e38dSTejun Heo 	class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
639def4e38dSTejun Heo 	while ((dev = class_dev_iter_next(&iter))) {
640def4e38dSTejun Heo 		struct gendisk *disk = dev_to_disk(dev);
641e71bf0d0STejun Heo 		struct disk_part_iter piter;
642e71bf0d0STejun Heo 		struct hd_struct *part;
6431f014290STejun Heo 		char name_buf[BDEVNAME_SIZE];
6441f014290STejun Heo 		char devt_buf[BDEVT_SIZE];
645*b5af921eSWill Drewry 		u8 uuid[PARTITION_META_INFO_UUIDLTH * 2 + 1];
646def4e38dSTejun Heo 
647def4e38dSTejun Heo 		/*
648def4e38dSTejun Heo 		 * Don't show empty devices or things that have been
649def4e38dSTejun Heo 		 * surpressed
650def4e38dSTejun Heo 		 */
651def4e38dSTejun Heo 		if (get_capacity(disk) == 0 ||
652def4e38dSTejun Heo 		    (disk->flags & GENHD_FL_SUPPRESS_PARTITION_INFO))
653def4e38dSTejun Heo 			continue;
654def4e38dSTejun Heo 
655def4e38dSTejun Heo 		/*
656def4e38dSTejun Heo 		 * Note, unlike /proc/partitions, I am showing the
657def4e38dSTejun Heo 		 * numbers in hex - the same format as the root=
658def4e38dSTejun Heo 		 * option takes.
659def4e38dSTejun Heo 		 */
660074a7acaSTejun Heo 		disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0);
661074a7acaSTejun Heo 		while ((part = disk_part_iter_next(&piter))) {
662074a7acaSTejun Heo 			bool is_part0 = part == &disk->part0;
663074a7acaSTejun Heo 
664*b5af921eSWill Drewry 			uuid[0] = 0;
665*b5af921eSWill Drewry 			if (part->info)
666*b5af921eSWill Drewry 				part_unpack_uuid(part->info->uuid, uuid);
667*b5af921eSWill Drewry 
668*b5af921eSWill Drewry 			printk("%s%s %10llu %s %s", is_part0 ? "" : "  ",
669074a7acaSTejun Heo 			       bdevt_str(part_devt(part), devt_buf),
670074a7acaSTejun Heo 			       (unsigned long long)part->nr_sects >> 1,
671*b5af921eSWill Drewry 			       disk_name(disk, part->partno, name_buf), uuid);
672074a7acaSTejun Heo 			if (is_part0) {
673def4e38dSTejun Heo 				if (disk->driverfs_dev != NULL &&
674def4e38dSTejun Heo 				    disk->driverfs_dev->driver != NULL)
675def4e38dSTejun Heo 					printk(" driver: %s\n",
676def4e38dSTejun Heo 					      disk->driverfs_dev->driver->name);
677def4e38dSTejun Heo 				else
678def4e38dSTejun Heo 					printk(" (driver?)\n");
679074a7acaSTejun Heo 			} else
680074a7acaSTejun Heo 				printk("\n");
681074a7acaSTejun Heo 		}
682e71bf0d0STejun Heo 		disk_part_iter_exit(&piter);
683def4e38dSTejun Heo 	}
684def4e38dSTejun Heo 	class_dev_iter_exit(&iter);
685dd2a345fSDave Gilbert }
686dd2a345fSDave Gilbert 
6871da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
6881da177e4SLinus Torvalds /* iterator */
689def4e38dSTejun Heo static void *disk_seqf_start(struct seq_file *seqf, loff_t *pos)
69068c4d4a7SGreg Kroah-Hartman {
691def4e38dSTejun Heo 	loff_t skip = *pos;
692def4e38dSTejun Heo 	struct class_dev_iter *iter;
693def4e38dSTejun Heo 	struct device *dev;
69468c4d4a7SGreg Kroah-Hartman 
695aeb3d3a8SHarvey Harrison 	iter = kmalloc(sizeof(*iter), GFP_KERNEL);
696def4e38dSTejun Heo 	if (!iter)
697def4e38dSTejun Heo 		return ERR_PTR(-ENOMEM);
698def4e38dSTejun Heo 
699def4e38dSTejun Heo 	seqf->private = iter;
700def4e38dSTejun Heo 	class_dev_iter_init(iter, &block_class, NULL, &disk_type);
701def4e38dSTejun Heo 	do {
702def4e38dSTejun Heo 		dev = class_dev_iter_next(iter);
703def4e38dSTejun Heo 		if (!dev)
704def4e38dSTejun Heo 			return NULL;
705def4e38dSTejun Heo 	} while (skip--);
706def4e38dSTejun Heo 
707def4e38dSTejun Heo 	return dev_to_disk(dev);
70868c4d4a7SGreg Kroah-Hartman }
70968c4d4a7SGreg Kroah-Hartman 
710def4e38dSTejun Heo static void *disk_seqf_next(struct seq_file *seqf, void *v, loff_t *pos)
7111da177e4SLinus Torvalds {
712edfaa7c3SKay Sievers 	struct device *dev;
71366c64afeSGreg Kroah-Hartman 
714def4e38dSTejun Heo 	(*pos)++;
715def4e38dSTejun Heo 	dev = class_dev_iter_next(seqf->private);
7162ac3cee5STejun Heo 	if (dev)
717edfaa7c3SKay Sievers 		return dev_to_disk(dev);
7182ac3cee5STejun Heo 
7191da177e4SLinus Torvalds 	return NULL;
7201da177e4SLinus Torvalds }
7211da177e4SLinus Torvalds 
722def4e38dSTejun Heo static void disk_seqf_stop(struct seq_file *seqf, void *v)
72327f30251SGreg Kroah-Hartman {
724def4e38dSTejun Heo 	struct class_dev_iter *iter = seqf->private;
725def4e38dSTejun Heo 
726def4e38dSTejun Heo 	/* stop is called even after start failed :-( */
727def4e38dSTejun Heo 	if (iter) {
728def4e38dSTejun Heo 		class_dev_iter_exit(iter);
729def4e38dSTejun Heo 		kfree(iter);
730def4e38dSTejun Heo 	}
73127f30251SGreg Kroah-Hartman }
73227f30251SGreg Kroah-Hartman 
733def4e38dSTejun Heo static void *show_partition_start(struct seq_file *seqf, loff_t *pos)
7341da177e4SLinus Torvalds {
735def4e38dSTejun Heo 	static void *p;
7361da177e4SLinus Torvalds 
737def4e38dSTejun Heo 	p = disk_seqf_start(seqf, pos);
738243294daSTejun Heo 	if (!IS_ERR(p) && p && !*pos)
739def4e38dSTejun Heo 		seq_puts(seqf, "major minor  #blocks  name\n\n");
740def4e38dSTejun Heo 	return p;
7411da177e4SLinus Torvalds }
7421da177e4SLinus Torvalds 
743cf771cb5STejun Heo static int show_partition(struct seq_file *seqf, void *v)
7441da177e4SLinus Torvalds {
7451da177e4SLinus Torvalds 	struct gendisk *sgp = v;
746e71bf0d0STejun Heo 	struct disk_part_iter piter;
747e71bf0d0STejun Heo 	struct hd_struct *part;
7481da177e4SLinus Torvalds 	char buf[BDEVNAME_SIZE];
7491da177e4SLinus Torvalds 
7501da177e4SLinus Torvalds 	/* Don't show non-partitionable removeable devices or empty devices */
751b5d0b9dfSTejun Heo 	if (!get_capacity(sgp) || (!disk_partitionable(sgp) &&
752f331c029STejun Heo 				   (sgp->flags & GENHD_FL_REMOVABLE)))
7531da177e4SLinus Torvalds 		return 0;
7541da177e4SLinus Torvalds 	if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO)
7551da177e4SLinus Torvalds 		return 0;
7561da177e4SLinus Torvalds 
7571da177e4SLinus Torvalds 	/* show the full disk and all non-0 size partitions of it */
758074a7acaSTejun Heo 	disk_part_iter_init(&piter, sgp, DISK_PITER_INCL_PART0);
759e71bf0d0STejun Heo 	while ((part = disk_part_iter_next(&piter)))
7601f014290STejun Heo 		seq_printf(seqf, "%4d  %7d %10llu %s\n",
761f331c029STejun Heo 			   MAJOR(part_devt(part)), MINOR(part_devt(part)),
762f331c029STejun Heo 			   (unsigned long long)part->nr_sects >> 1,
763f331c029STejun Heo 			   disk_name(sgp, part->partno, buf));
764e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
7651da177e4SLinus Torvalds 
7661da177e4SLinus Torvalds 	return 0;
7671da177e4SLinus Torvalds }
7681da177e4SLinus Torvalds 
769f500975aSAlexey Dobriyan static const struct seq_operations partitions_op = {
770def4e38dSTejun Heo 	.start	= show_partition_start,
771def4e38dSTejun Heo 	.next	= disk_seqf_next,
772def4e38dSTejun Heo 	.stop	= disk_seqf_stop,
7731da177e4SLinus Torvalds 	.show	= show_partition
7741da177e4SLinus Torvalds };
775f500975aSAlexey Dobriyan 
776f500975aSAlexey Dobriyan static int partitions_open(struct inode *inode, struct file *file)
777f500975aSAlexey Dobriyan {
778f500975aSAlexey Dobriyan 	return seq_open(file, &partitions_op);
779f500975aSAlexey Dobriyan }
780f500975aSAlexey Dobriyan 
781f500975aSAlexey Dobriyan static const struct file_operations proc_partitions_operations = {
782f500975aSAlexey Dobriyan 	.open		= partitions_open,
783f500975aSAlexey Dobriyan 	.read		= seq_read,
784f500975aSAlexey Dobriyan 	.llseek		= seq_lseek,
785f500975aSAlexey Dobriyan 	.release	= seq_release,
786f500975aSAlexey Dobriyan };
7871da177e4SLinus Torvalds #endif
7881da177e4SLinus Torvalds 
7891da177e4SLinus Torvalds 
790cf771cb5STejun Heo static struct kobject *base_probe(dev_t devt, int *partno, void *data)
7911da177e4SLinus Torvalds {
792edfaa7c3SKay Sievers 	if (request_module("block-major-%d-%d", MAJOR(devt), MINOR(devt)) > 0)
7931da177e4SLinus Torvalds 		/* Make old-style 2.4 aliases work */
794edfaa7c3SKay Sievers 		request_module("block-major-%d", MAJOR(devt));
7951da177e4SLinus Torvalds 	return NULL;
7961da177e4SLinus Torvalds }
7971da177e4SLinus Torvalds 
7981da177e4SLinus Torvalds static int __init genhd_device_init(void)
7991da177e4SLinus Torvalds {
800e105b8bfSDan Williams 	int error;
801e105b8bfSDan Williams 
802e105b8bfSDan Williams 	block_class.dev_kobj = sysfs_dev_block_kobj;
803e105b8bfSDan Williams 	error = class_register(&block_class);
804ee27a558SRoland McGrath 	if (unlikely(error))
805ee27a558SRoland McGrath 		return error;
806edfaa7c3SKay Sievers 	bdev_map = kobj_map_init(base_probe, &block_class_lock);
8071da177e4SLinus Torvalds 	blk_dev_init();
808edfaa7c3SKay Sievers 
809561ec68eSZhang, Yanmin 	register_blkdev(BLOCK_EXT_MAJOR, "blkext");
810561ec68eSZhang, Yanmin 
811edfaa7c3SKay Sievers #ifndef CONFIG_SYSFS_DEPRECATED
812edfaa7c3SKay Sievers 	/* create top-level block dir */
813edfaa7c3SKay Sievers 	block_depr = kobject_create_and_add("block", NULL);
814edfaa7c3SKay Sievers #endif
815830d3cfbSGreg Kroah-Hartman 	return 0;
8161da177e4SLinus Torvalds }
8171da177e4SLinus Torvalds 
8181da177e4SLinus Torvalds subsys_initcall(genhd_device_init);
8191da177e4SLinus Torvalds 
820edfaa7c3SKay Sievers static ssize_t disk_range_show(struct device *dev,
821edfaa7c3SKay Sievers 			       struct device_attribute *attr, char *buf)
8221da177e4SLinus Torvalds {
823edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
8241da177e4SLinus Torvalds 
825edfaa7c3SKay Sievers 	return sprintf(buf, "%d\n", disk->minors);
8261da177e4SLinus Torvalds }
8271da177e4SLinus Torvalds 
8281f014290STejun Heo static ssize_t disk_ext_range_show(struct device *dev,
8291f014290STejun Heo 				   struct device_attribute *attr, char *buf)
8301f014290STejun Heo {
8311f014290STejun Heo 	struct gendisk *disk = dev_to_disk(dev);
8321f014290STejun Heo 
833b5d0b9dfSTejun Heo 	return sprintf(buf, "%d\n", disk_max_parts(disk));
8341f014290STejun Heo }
8351f014290STejun Heo 
836edfaa7c3SKay Sievers static ssize_t disk_removable_show(struct device *dev,
837edfaa7c3SKay Sievers 				   struct device_attribute *attr, char *buf)
838a7fd6706SKay Sievers {
839edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
840a7fd6706SKay Sievers 
841edfaa7c3SKay Sievers 	return sprintf(buf, "%d\n",
8421da177e4SLinus Torvalds 		       (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0));
843edfaa7c3SKay Sievers }
8441da177e4SLinus Torvalds 
8451c9ce527SKay Sievers static ssize_t disk_ro_show(struct device *dev,
8461c9ce527SKay Sievers 				   struct device_attribute *attr, char *buf)
8471c9ce527SKay Sievers {
8481c9ce527SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
8491c9ce527SKay Sievers 
850b7db9956STejun Heo 	return sprintf(buf, "%d\n", get_disk_ro(disk) ? 1 : 0);
8511c9ce527SKay Sievers }
8521c9ce527SKay Sievers 
853edfaa7c3SKay Sievers static ssize_t disk_capability_show(struct device *dev,
854edfaa7c3SKay Sievers 				    struct device_attribute *attr, char *buf)
85586ce18d7SKristen Carlson Accardi {
856edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
857edfaa7c3SKay Sievers 
858edfaa7c3SKay Sievers 	return sprintf(buf, "%x\n", disk->flags);
85986ce18d7SKristen Carlson Accardi }
860edfaa7c3SKay Sievers 
861c72758f3SMartin K. Petersen static ssize_t disk_alignment_offset_show(struct device *dev,
862c72758f3SMartin K. Petersen 					  struct device_attribute *attr,
863c72758f3SMartin K. Petersen 					  char *buf)
864c72758f3SMartin K. Petersen {
865c72758f3SMartin K. Petersen 	struct gendisk *disk = dev_to_disk(dev);
866c72758f3SMartin K. Petersen 
867c72758f3SMartin K. Petersen 	return sprintf(buf, "%d\n", queue_alignment_offset(disk->queue));
868c72758f3SMartin K. Petersen }
869c72758f3SMartin K. Petersen 
87086b37281SMartin K. Petersen static ssize_t disk_discard_alignment_show(struct device *dev,
87186b37281SMartin K. Petersen 					   struct device_attribute *attr,
87286b37281SMartin K. Petersen 					   char *buf)
87386b37281SMartin K. Petersen {
87486b37281SMartin K. Petersen 	struct gendisk *disk = dev_to_disk(dev);
87586b37281SMartin K. Petersen 
876dd3d145dSMartin K. Petersen 	return sprintf(buf, "%d\n", queue_discard_alignment(disk->queue));
87786b37281SMartin K. Petersen }
87886b37281SMartin K. Petersen 
879edfaa7c3SKay Sievers static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL);
8801f014290STejun Heo static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL);
881edfaa7c3SKay Sievers static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL);
8821c9ce527SKay Sievers static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL);
883e5610521STejun Heo static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL);
884c72758f3SMartin K. Petersen static DEVICE_ATTR(alignment_offset, S_IRUGO, disk_alignment_offset_show, NULL);
88586b37281SMartin K. Petersen static DEVICE_ATTR(discard_alignment, S_IRUGO, disk_discard_alignment_show,
88686b37281SMartin K. Petersen 		   NULL);
887edfaa7c3SKay Sievers static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL);
888074a7acaSTejun Heo static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL);
889316d315bSNikanth Karthikesan static DEVICE_ATTR(inflight, S_IRUGO, part_inflight_show, NULL);
890c17bb495SAkinobu Mita #ifdef CONFIG_FAIL_MAKE_REQUEST
891edfaa7c3SKay Sievers static struct device_attribute dev_attr_fail =
892eddb2e26STejun Heo 	__ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store);
893c17bb495SAkinobu Mita #endif
894581d4e28SJens Axboe #ifdef CONFIG_FAIL_IO_TIMEOUT
895581d4e28SJens Axboe static struct device_attribute dev_attr_fail_timeout =
896581d4e28SJens Axboe 	__ATTR(io-timeout-fail,  S_IRUGO|S_IWUSR, part_timeout_show,
897581d4e28SJens Axboe 		part_timeout_store);
898581d4e28SJens Axboe #endif
899edfaa7c3SKay Sievers 
900edfaa7c3SKay Sievers static struct attribute *disk_attrs[] = {
901edfaa7c3SKay Sievers 	&dev_attr_range.attr,
9021f014290STejun Heo 	&dev_attr_ext_range.attr,
903edfaa7c3SKay Sievers 	&dev_attr_removable.attr,
9041c9ce527SKay Sievers 	&dev_attr_ro.attr,
905edfaa7c3SKay Sievers 	&dev_attr_size.attr,
906c72758f3SMartin K. Petersen 	&dev_attr_alignment_offset.attr,
90786b37281SMartin K. Petersen 	&dev_attr_discard_alignment.attr,
908edfaa7c3SKay Sievers 	&dev_attr_capability.attr,
909edfaa7c3SKay Sievers 	&dev_attr_stat.attr,
910316d315bSNikanth Karthikesan 	&dev_attr_inflight.attr,
911edfaa7c3SKay Sievers #ifdef CONFIG_FAIL_MAKE_REQUEST
912edfaa7c3SKay Sievers 	&dev_attr_fail.attr,
913edfaa7c3SKay Sievers #endif
914581d4e28SJens Axboe #ifdef CONFIG_FAIL_IO_TIMEOUT
915581d4e28SJens Axboe 	&dev_attr_fail_timeout.attr,
916581d4e28SJens Axboe #endif
917edfaa7c3SKay Sievers 	NULL
9181da177e4SLinus Torvalds };
9191da177e4SLinus Torvalds 
920edfaa7c3SKay Sievers static struct attribute_group disk_attr_group = {
921edfaa7c3SKay Sievers 	.attrs = disk_attrs,
922edfaa7c3SKay Sievers };
923edfaa7c3SKay Sievers 
924a4dbd674SDavid Brownell static const struct attribute_group *disk_attr_groups[] = {
925edfaa7c3SKay Sievers 	&disk_attr_group,
926edfaa7c3SKay Sievers 	NULL
927edfaa7c3SKay Sievers };
928edfaa7c3SKay Sievers 
929540eed56STejun Heo static void disk_free_ptbl_rcu_cb(struct rcu_head *head)
930540eed56STejun Heo {
931540eed56STejun Heo 	struct disk_part_tbl *ptbl =
932540eed56STejun Heo 		container_of(head, struct disk_part_tbl, rcu_head);
933540eed56STejun Heo 
934540eed56STejun Heo 	kfree(ptbl);
935540eed56STejun Heo }
936540eed56STejun Heo 
937540eed56STejun Heo /**
938540eed56STejun Heo  * disk_replace_part_tbl - replace disk->part_tbl in RCU-safe way
939540eed56STejun Heo  * @disk: disk to replace part_tbl for
940540eed56STejun Heo  * @new_ptbl: new part_tbl to install
941540eed56STejun Heo  *
942540eed56STejun Heo  * Replace disk->part_tbl with @new_ptbl in RCU-safe way.  The
943540eed56STejun Heo  * original ptbl is freed using RCU callback.
944540eed56STejun Heo  *
945540eed56STejun Heo  * LOCKING:
946540eed56STejun Heo  * Matching bd_mutx locked.
947540eed56STejun Heo  */
948540eed56STejun Heo static void disk_replace_part_tbl(struct gendisk *disk,
949540eed56STejun Heo 				  struct disk_part_tbl *new_ptbl)
950540eed56STejun Heo {
951540eed56STejun Heo 	struct disk_part_tbl *old_ptbl = disk->part_tbl;
952540eed56STejun Heo 
953540eed56STejun Heo 	rcu_assign_pointer(disk->part_tbl, new_ptbl);
954a6f23657SJens Axboe 
955a6f23657SJens Axboe 	if (old_ptbl) {
956a6f23657SJens Axboe 		rcu_assign_pointer(old_ptbl->last_lookup, NULL);
957540eed56STejun Heo 		call_rcu(&old_ptbl->rcu_head, disk_free_ptbl_rcu_cb);
958540eed56STejun Heo 	}
959a6f23657SJens Axboe }
960540eed56STejun Heo 
961540eed56STejun Heo /**
962540eed56STejun Heo  * disk_expand_part_tbl - expand disk->part_tbl
963540eed56STejun Heo  * @disk: disk to expand part_tbl for
964540eed56STejun Heo  * @partno: expand such that this partno can fit in
965540eed56STejun Heo  *
966540eed56STejun Heo  * Expand disk->part_tbl such that @partno can fit in.  disk->part_tbl
967540eed56STejun Heo  * uses RCU to allow unlocked dereferencing for stats and other stuff.
968540eed56STejun Heo  *
969540eed56STejun Heo  * LOCKING:
970540eed56STejun Heo  * Matching bd_mutex locked, might sleep.
971540eed56STejun Heo  *
972540eed56STejun Heo  * RETURNS:
973540eed56STejun Heo  * 0 on success, -errno on failure.
974540eed56STejun Heo  */
975540eed56STejun Heo int disk_expand_part_tbl(struct gendisk *disk, int partno)
976540eed56STejun Heo {
977540eed56STejun Heo 	struct disk_part_tbl *old_ptbl = disk->part_tbl;
978540eed56STejun Heo 	struct disk_part_tbl *new_ptbl;
979540eed56STejun Heo 	int len = old_ptbl ? old_ptbl->len : 0;
980540eed56STejun Heo 	int target = partno + 1;
981540eed56STejun Heo 	size_t size;
982540eed56STejun Heo 	int i;
983540eed56STejun Heo 
984540eed56STejun Heo 	/* disk_max_parts() is zero during initialization, ignore if so */
985540eed56STejun Heo 	if (disk_max_parts(disk) && target > disk_max_parts(disk))
986540eed56STejun Heo 		return -EINVAL;
987540eed56STejun Heo 
988540eed56STejun Heo 	if (target <= len)
989540eed56STejun Heo 		return 0;
990540eed56STejun Heo 
991540eed56STejun Heo 	size = sizeof(*new_ptbl) + target * sizeof(new_ptbl->part[0]);
992540eed56STejun Heo 	new_ptbl = kzalloc_node(size, GFP_KERNEL, disk->node_id);
993540eed56STejun Heo 	if (!new_ptbl)
994540eed56STejun Heo 		return -ENOMEM;
995540eed56STejun Heo 
996540eed56STejun Heo 	new_ptbl->len = target;
997540eed56STejun Heo 
998540eed56STejun Heo 	for (i = 0; i < len; i++)
999540eed56STejun Heo 		rcu_assign_pointer(new_ptbl->part[i], old_ptbl->part[i]);
1000540eed56STejun Heo 
1001540eed56STejun Heo 	disk_replace_part_tbl(disk, new_ptbl);
1002540eed56STejun Heo 	return 0;
1003540eed56STejun Heo }
1004540eed56STejun Heo 
1005edfaa7c3SKay Sievers static void disk_release(struct device *dev)
10061da177e4SLinus Torvalds {
1007edfaa7c3SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
1008edfaa7c3SKay Sievers 
10091da177e4SLinus Torvalds 	kfree(disk->random);
1010540eed56STejun Heo 	disk_replace_part_tbl(disk, NULL);
1011074a7acaSTejun Heo 	free_part_stats(&disk->part0);
10126d1d8050SWill Drewry 	free_part_info(&disk->part0);
10131da177e4SLinus Torvalds 	kfree(disk);
10141da177e4SLinus Torvalds }
1015edfaa7c3SKay Sievers struct class block_class = {
1016edfaa7c3SKay Sievers 	.name		= "block",
10171da177e4SLinus Torvalds };
10181da177e4SLinus Torvalds 
1019e454cea2SKay Sievers static char *block_devnode(struct device *dev, mode_t *mode)
1020b03f38b6SKay Sievers {
1021b03f38b6SKay Sievers 	struct gendisk *disk = dev_to_disk(dev);
1022b03f38b6SKay Sievers 
1023e454cea2SKay Sievers 	if (disk->devnode)
1024e454cea2SKay Sievers 		return disk->devnode(disk, mode);
1025b03f38b6SKay Sievers 	return NULL;
1026b03f38b6SKay Sievers }
1027b03f38b6SKay Sievers 
10281826eadfSAdrian Bunk static struct device_type disk_type = {
1029edfaa7c3SKay Sievers 	.name		= "disk",
1030edfaa7c3SKay Sievers 	.groups		= disk_attr_groups,
1031edfaa7c3SKay Sievers 	.release	= disk_release,
1032e454cea2SKay Sievers 	.devnode	= block_devnode,
10331da177e4SLinus Torvalds };
10341da177e4SLinus Torvalds 
1035a6e2ba88SRandy Dunlap #ifdef CONFIG_PROC_FS
1036cf771cb5STejun Heo /*
1037cf771cb5STejun Heo  * aggregate disk stat collector.  Uses the same stats that the sysfs
1038cf771cb5STejun Heo  * entries do, above, but makes them available through one seq_file.
1039cf771cb5STejun Heo  *
1040cf771cb5STejun Heo  * The output looks suspiciously like /proc/partitions with a bunch of
1041cf771cb5STejun Heo  * extra fields.
1042cf771cb5STejun Heo  */
1043cf771cb5STejun Heo static int diskstats_show(struct seq_file *seqf, void *v)
10441da177e4SLinus Torvalds {
10451da177e4SLinus Torvalds 	struct gendisk *gp = v;
1046e71bf0d0STejun Heo 	struct disk_part_iter piter;
1047e71bf0d0STejun Heo 	struct hd_struct *hd;
10481da177e4SLinus Torvalds 	char buf[BDEVNAME_SIZE];
1049c9959059STejun Heo 	int cpu;
10501da177e4SLinus Torvalds 
10511da177e4SLinus Torvalds 	/*
1052ed9e1982STejun Heo 	if (&disk_to_dev(gp)->kobj.entry == block_class.devices.next)
1053cf771cb5STejun Heo 		seq_puts(seqf,	"major minor name"
10541da177e4SLinus Torvalds 				"     rio rmerge rsect ruse wio wmerge "
10551da177e4SLinus Torvalds 				"wsect wuse running use aveq"
10561da177e4SLinus Torvalds 				"\n\n");
10571da177e4SLinus Torvalds 	*/
10581da177e4SLinus Torvalds 
105971982a40STejun Heo 	disk_part_iter_init(&piter, gp, DISK_PITER_INCL_EMPTY_PART0);
1060e71bf0d0STejun Heo 	while ((hd = disk_part_iter_next(&piter))) {
1061074a7acaSTejun Heo 		cpu = part_stat_lock();
1062c9959059STejun Heo 		part_round_stats(cpu, hd);
1063074a7acaSTejun Heo 		part_stat_unlock();
10641f014290STejun Heo 		seq_printf(seqf, "%4d %7d %s %lu %lu %llu "
106528f39d55SJerome Marchand 			   "%u %lu %lu %llu %u %u %u %u\n",
1066f331c029STejun Heo 			   MAJOR(part_devt(hd)), MINOR(part_devt(hd)),
1067f331c029STejun Heo 			   disk_name(gp, hd->partno, buf),
106828f39d55SJerome Marchand 			   part_stat_read(hd, ios[0]),
106928f39d55SJerome Marchand 			   part_stat_read(hd, merges[0]),
107028f39d55SJerome Marchand 			   (unsigned long long)part_stat_read(hd, sectors[0]),
107128f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, ticks[0])),
107228f39d55SJerome Marchand 			   part_stat_read(hd, ios[1]),
107328f39d55SJerome Marchand 			   part_stat_read(hd, merges[1]),
107428f39d55SJerome Marchand 			   (unsigned long long)part_stat_read(hd, sectors[1]),
107528f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, ticks[1])),
1076316d315bSNikanth Karthikesan 			   part_in_flight(hd),
107728f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, io_ticks)),
107828f39d55SJerome Marchand 			   jiffies_to_msecs(part_stat_read(hd, time_in_queue))
107928f39d55SJerome Marchand 			);
10801da177e4SLinus Torvalds 	}
1081e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
10821da177e4SLinus Torvalds 
10831da177e4SLinus Torvalds 	return 0;
10841da177e4SLinus Torvalds }
10851da177e4SLinus Torvalds 
108631d85ab2SAlexey Dobriyan static const struct seq_operations diskstats_op = {
1087def4e38dSTejun Heo 	.start	= disk_seqf_start,
1088def4e38dSTejun Heo 	.next	= disk_seqf_next,
1089def4e38dSTejun Heo 	.stop	= disk_seqf_stop,
10901da177e4SLinus Torvalds 	.show	= diskstats_show
10911da177e4SLinus Torvalds };
1092f500975aSAlexey Dobriyan 
109331d85ab2SAlexey Dobriyan static int diskstats_open(struct inode *inode, struct file *file)
109431d85ab2SAlexey Dobriyan {
109531d85ab2SAlexey Dobriyan 	return seq_open(file, &diskstats_op);
109631d85ab2SAlexey Dobriyan }
109731d85ab2SAlexey Dobriyan 
109831d85ab2SAlexey Dobriyan static const struct file_operations proc_diskstats_operations = {
109931d85ab2SAlexey Dobriyan 	.open		= diskstats_open,
110031d85ab2SAlexey Dobriyan 	.read		= seq_read,
110131d85ab2SAlexey Dobriyan 	.llseek		= seq_lseek,
110231d85ab2SAlexey Dobriyan 	.release	= seq_release,
110331d85ab2SAlexey Dobriyan };
110431d85ab2SAlexey Dobriyan 
1105f500975aSAlexey Dobriyan static int __init proc_genhd_init(void)
1106f500975aSAlexey Dobriyan {
110731d85ab2SAlexey Dobriyan 	proc_create("diskstats", 0, NULL, &proc_diskstats_operations);
1108f500975aSAlexey Dobriyan 	proc_create("partitions", 0, NULL, &proc_partitions_operations);
1109f500975aSAlexey Dobriyan 	return 0;
1110f500975aSAlexey Dobriyan }
1111f500975aSAlexey Dobriyan module_init(proc_genhd_init);
1112a6e2ba88SRandy Dunlap #endif /* CONFIG_PROC_FS */
11131da177e4SLinus Torvalds 
11148ce7ad7bSKristen Carlson Accardi static void media_change_notify_thread(struct work_struct *work)
11158ce7ad7bSKristen Carlson Accardi {
11168ce7ad7bSKristen Carlson Accardi 	struct gendisk *gd = container_of(work, struct gendisk, async_notify);
11178ce7ad7bSKristen Carlson Accardi 	char event[] = "MEDIA_CHANGE=1";
11188ce7ad7bSKristen Carlson Accardi 	char *envp[] = { event, NULL };
11198ce7ad7bSKristen Carlson Accardi 
11208ce7ad7bSKristen Carlson Accardi 	/*
11218ce7ad7bSKristen Carlson Accardi 	 * set enviroment vars to indicate which event this is for
11228ce7ad7bSKristen Carlson Accardi 	 * so that user space will know to go check the media status.
11238ce7ad7bSKristen Carlson Accardi 	 */
1124ed9e1982STejun Heo 	kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp);
11258ce7ad7bSKristen Carlson Accardi 	put_device(gd->driverfs_dev);
11268ce7ad7bSKristen Carlson Accardi }
11278ce7ad7bSKristen Carlson Accardi 
11281826eadfSAdrian Bunk #if 0
11298ce7ad7bSKristen Carlson Accardi void genhd_media_change_notify(struct gendisk *disk)
11308ce7ad7bSKristen Carlson Accardi {
11318ce7ad7bSKristen Carlson Accardi 	get_device(disk->driverfs_dev);
11328ce7ad7bSKristen Carlson Accardi 	schedule_work(&disk->async_notify);
11338ce7ad7bSKristen Carlson Accardi }
11348ce7ad7bSKristen Carlson Accardi EXPORT_SYMBOL_GPL(genhd_media_change_notify);
11351826eadfSAdrian Bunk #endif  /*  0  */
11368ce7ad7bSKristen Carlson Accardi 
1137cf771cb5STejun Heo dev_t blk_lookup_devt(const char *name, int partno)
1138edfaa7c3SKay Sievers {
1139edfaa7c3SKay Sievers 	dev_t devt = MKDEV(0, 0);
1140def4e38dSTejun Heo 	struct class_dev_iter iter;
1141def4e38dSTejun Heo 	struct device *dev;
1142edfaa7c3SKay Sievers 
1143def4e38dSTejun Heo 	class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
1144def4e38dSTejun Heo 	while ((dev = class_dev_iter_next(&iter))) {
1145def4e38dSTejun Heo 		struct gendisk *disk = dev_to_disk(dev);
1146548b10ebSTejun Heo 		struct hd_struct *part;
1147def4e38dSTejun Heo 
11483ada8b7eSKay Sievers 		if (strcmp(dev_name(dev), name))
1149f331c029STejun Heo 			continue;
1150f331c029STejun Heo 
115141b8c853SNeil Brown 		if (partno < disk->minors) {
115241b8c853SNeil Brown 			/* We need to return the right devno, even
115341b8c853SNeil Brown 			 * if the partition doesn't exist yet.
115441b8c853SNeil Brown 			 */
115541b8c853SNeil Brown 			devt = MKDEV(MAJOR(dev->devt),
115641b8c853SNeil Brown 				     MINOR(dev->devt) + partno);
115741b8c853SNeil Brown 			break;
115841b8c853SNeil Brown 		}
1159e71bf0d0STejun Heo 		part = disk_get_part(disk, partno);
11602bbedcb4STejun Heo 		if (part) {
1161f331c029STejun Heo 			devt = part_devt(part);
1162e71bf0d0STejun Heo 			disk_put_part(part);
1163f331c029STejun Heo 			break;
1164def4e38dSTejun Heo 		}
1165548b10ebSTejun Heo 		disk_put_part(part);
1166548b10ebSTejun Heo 	}
1167def4e38dSTejun Heo 	class_dev_iter_exit(&iter);
1168edfaa7c3SKay Sievers 	return devt;
1169edfaa7c3SKay Sievers }
1170edfaa7c3SKay Sievers EXPORT_SYMBOL(blk_lookup_devt);
1171edfaa7c3SKay Sievers 
11721da177e4SLinus Torvalds struct gendisk *alloc_disk(int minors)
11731da177e4SLinus Torvalds {
11741946089aSChristoph Lameter 	return alloc_disk_node(minors, -1);
11751946089aSChristoph Lameter }
1176689d6facSTejun Heo EXPORT_SYMBOL(alloc_disk);
11771946089aSChristoph Lameter 
11781946089aSChristoph Lameter struct gendisk *alloc_disk_node(int minors, int node_id)
11791946089aSChristoph Lameter {
11801946089aSChristoph Lameter 	struct gendisk *disk;
11811946089aSChristoph Lameter 
118294f6030cSChristoph Lameter 	disk = kmalloc_node(sizeof(struct gendisk),
118394f6030cSChristoph Lameter 				GFP_KERNEL | __GFP_ZERO, node_id);
11841da177e4SLinus Torvalds 	if (disk) {
1185074a7acaSTejun Heo 		if (!init_part_stats(&disk->part0)) {
11861da177e4SLinus Torvalds 			kfree(disk);
11871da177e4SLinus Torvalds 			return NULL;
11881da177e4SLinus Torvalds 		}
1189bf91db18SCheng Renquan 		disk->node_id = node_id;
1190540eed56STejun Heo 		if (disk_expand_part_tbl(disk, 0)) {
1191074a7acaSTejun Heo 			free_part_stats(&disk->part0);
11921da177e4SLinus Torvalds 			kfree(disk);
11931da177e4SLinus Torvalds 			return NULL;
11941da177e4SLinus Torvalds 		}
1195540eed56STejun Heo 		disk->part_tbl->part[0] = &disk->part0;
1196b5d0b9dfSTejun Heo 
11971da177e4SLinus Torvalds 		disk->minors = minors;
11981da177e4SLinus Torvalds 		rand_initialize_disk(disk);
1199ed9e1982STejun Heo 		disk_to_dev(disk)->class = &block_class;
1200ed9e1982STejun Heo 		disk_to_dev(disk)->type = &disk_type;
1201ed9e1982STejun Heo 		device_initialize(disk_to_dev(disk));
12028ce7ad7bSKristen Carlson Accardi 		INIT_WORK(&disk->async_notify,
12038ce7ad7bSKristen Carlson Accardi 			media_change_notify_thread);
12041da177e4SLinus Torvalds 	}
12051da177e4SLinus Torvalds 	return disk;
12061da177e4SLinus Torvalds }
12071946089aSChristoph Lameter EXPORT_SYMBOL(alloc_disk_node);
12081da177e4SLinus Torvalds 
12091da177e4SLinus Torvalds struct kobject *get_disk(struct gendisk *disk)
12101da177e4SLinus Torvalds {
12111da177e4SLinus Torvalds 	struct module *owner;
12121da177e4SLinus Torvalds 	struct kobject *kobj;
12131da177e4SLinus Torvalds 
12141da177e4SLinus Torvalds 	if (!disk->fops)
12151da177e4SLinus Torvalds 		return NULL;
12161da177e4SLinus Torvalds 	owner = disk->fops->owner;
12171da177e4SLinus Torvalds 	if (owner && !try_module_get(owner))
12181da177e4SLinus Torvalds 		return NULL;
1219ed9e1982STejun Heo 	kobj = kobject_get(&disk_to_dev(disk)->kobj);
12201da177e4SLinus Torvalds 	if (kobj == NULL) {
12211da177e4SLinus Torvalds 		module_put(owner);
12221da177e4SLinus Torvalds 		return NULL;
12231da177e4SLinus Torvalds 	}
12241da177e4SLinus Torvalds 	return kobj;
12251da177e4SLinus Torvalds 
12261da177e4SLinus Torvalds }
12271da177e4SLinus Torvalds 
12281da177e4SLinus Torvalds EXPORT_SYMBOL(get_disk);
12291da177e4SLinus Torvalds 
12301da177e4SLinus Torvalds void put_disk(struct gendisk *disk)
12311da177e4SLinus Torvalds {
12321da177e4SLinus Torvalds 	if (disk)
1233ed9e1982STejun Heo 		kobject_put(&disk_to_dev(disk)->kobj);
12341da177e4SLinus Torvalds }
12351da177e4SLinus Torvalds 
12361da177e4SLinus Torvalds EXPORT_SYMBOL(put_disk);
12371da177e4SLinus Torvalds 
1238e3264a4dSHannes Reinecke static void set_disk_ro_uevent(struct gendisk *gd, int ro)
1239e3264a4dSHannes Reinecke {
1240e3264a4dSHannes Reinecke 	char event[] = "DISK_RO=1";
1241e3264a4dSHannes Reinecke 	char *envp[] = { event, NULL };
1242e3264a4dSHannes Reinecke 
1243e3264a4dSHannes Reinecke 	if (!ro)
1244e3264a4dSHannes Reinecke 		event[8] = '0';
1245e3264a4dSHannes Reinecke 	kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp);
1246e3264a4dSHannes Reinecke }
1247e3264a4dSHannes Reinecke 
12481da177e4SLinus Torvalds void set_device_ro(struct block_device *bdev, int flag)
12491da177e4SLinus Torvalds {
12501da177e4SLinus Torvalds 	bdev->bd_part->policy = flag;
12511da177e4SLinus Torvalds }
12521da177e4SLinus Torvalds 
12531da177e4SLinus Torvalds EXPORT_SYMBOL(set_device_ro);
12541da177e4SLinus Torvalds 
12551da177e4SLinus Torvalds void set_disk_ro(struct gendisk *disk, int flag)
12561da177e4SLinus Torvalds {
1257e71bf0d0STejun Heo 	struct disk_part_iter piter;
1258e71bf0d0STejun Heo 	struct hd_struct *part;
1259e71bf0d0STejun Heo 
1260e3264a4dSHannes Reinecke 	if (disk->part0.policy != flag) {
1261e3264a4dSHannes Reinecke 		set_disk_ro_uevent(disk, flag);
1262e3264a4dSHannes Reinecke 		disk->part0.policy = flag;
1263e3264a4dSHannes Reinecke 	}
1264e3264a4dSHannes Reinecke 
1265e3264a4dSHannes Reinecke 	disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY);
1266e71bf0d0STejun Heo 	while ((part = disk_part_iter_next(&piter)))
1267e71bf0d0STejun Heo 		part->policy = flag;
1268e71bf0d0STejun Heo 	disk_part_iter_exit(&piter);
12691da177e4SLinus Torvalds }
12701da177e4SLinus Torvalds 
12711da177e4SLinus Torvalds EXPORT_SYMBOL(set_disk_ro);
12721da177e4SLinus Torvalds 
12731da177e4SLinus Torvalds int bdev_read_only(struct block_device *bdev)
12741da177e4SLinus Torvalds {
12751da177e4SLinus Torvalds 	if (!bdev)
12761da177e4SLinus Torvalds 		return 0;
12771da177e4SLinus Torvalds 	return bdev->bd_part->policy;
12781da177e4SLinus Torvalds }
12791da177e4SLinus Torvalds 
12801da177e4SLinus Torvalds EXPORT_SYMBOL(bdev_read_only);
12811da177e4SLinus Torvalds 
1282cf771cb5STejun Heo int invalidate_partition(struct gendisk *disk, int partno)
12831da177e4SLinus Torvalds {
12841da177e4SLinus Torvalds 	int res = 0;
1285cf771cb5STejun Heo 	struct block_device *bdev = bdget_disk(disk, partno);
12861da177e4SLinus Torvalds 	if (bdev) {
12872ef41634SChristoph Hellwig 		fsync_bdev(bdev);
12882ef41634SChristoph Hellwig 		res = __invalidate_device(bdev);
12891da177e4SLinus Torvalds 		bdput(bdev);
12901da177e4SLinus Torvalds 	}
12911da177e4SLinus Torvalds 	return res;
12921da177e4SLinus Torvalds }
12931da177e4SLinus Torvalds 
12941da177e4SLinus Torvalds EXPORT_SYMBOL(invalidate_partition);
1295