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