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 } 5991da177e4SLinus Torvalds 600f331c029STejun Heo /** 601f331c029STejun Heo * bdget_disk - do bdget() by gendisk and partition number 602f331c029STejun Heo * @disk: gendisk of interest 603f331c029STejun Heo * @partno: partition number 604f331c029STejun Heo * 605f331c029STejun Heo * Find partition @partno from @disk, do bdget() on it. 606f331c029STejun Heo * 607f331c029STejun Heo * CONTEXT: 608f331c029STejun Heo * Don't care. 609f331c029STejun Heo * 610f331c029STejun Heo * RETURNS: 611f331c029STejun Heo * Resulting block_device on success, NULL on failure. 612f331c029STejun Heo */ 613aeb3d3a8SHarvey Harrison struct block_device *bdget_disk(struct gendisk *disk, int partno) 614f331c029STejun Heo { 615e71bf0d0STejun Heo struct hd_struct *part; 616548b10ebSTejun Heo struct block_device *bdev = NULL; 617f331c029STejun Heo 618e71bf0d0STejun Heo part = disk_get_part(disk, partno); 6192bbedcb4STejun Heo if (part) 620548b10ebSTejun Heo bdev = bdget(part_devt(part)); 621e71bf0d0STejun Heo disk_put_part(part); 622f331c029STejun Heo 623548b10ebSTejun Heo return bdev; 624f331c029STejun Heo } 625f331c029STejun Heo EXPORT_SYMBOL(bdget_disk); 626f331c029STejun Heo 627dd2a345fSDave Gilbert /* 6285c6f35c5SGreg Kroah-Hartman * print a full list of all partitions - intended for places where the root 6295c6f35c5SGreg Kroah-Hartman * filesystem can't be mounted and thus to give the victim some idea of what 6305c6f35c5SGreg Kroah-Hartman * went wrong 6315c6f35c5SGreg Kroah-Hartman */ 6325c6f35c5SGreg Kroah-Hartman void __init printk_all_partitions(void) 6335c6f35c5SGreg Kroah-Hartman { 634def4e38dSTejun Heo struct class_dev_iter iter; 635def4e38dSTejun Heo struct device *dev; 636def4e38dSTejun Heo 637def4e38dSTejun Heo class_dev_iter_init(&iter, &block_class, NULL, &disk_type); 638def4e38dSTejun Heo while ((dev = class_dev_iter_next(&iter))) { 639def4e38dSTejun Heo struct gendisk *disk = dev_to_disk(dev); 640e71bf0d0STejun Heo struct disk_part_iter piter; 641e71bf0d0STejun Heo struct hd_struct *part; 6421f014290STejun Heo char name_buf[BDEVNAME_SIZE]; 6431f014290STejun Heo char devt_buf[BDEVT_SIZE]; 644def4e38dSTejun Heo 645def4e38dSTejun Heo /* 646def4e38dSTejun Heo * Don't show empty devices or things that have been 647def4e38dSTejun Heo * surpressed 648def4e38dSTejun Heo */ 649def4e38dSTejun Heo if (get_capacity(disk) == 0 || 650def4e38dSTejun Heo (disk->flags & GENHD_FL_SUPPRESS_PARTITION_INFO)) 651def4e38dSTejun Heo continue; 652def4e38dSTejun Heo 653def4e38dSTejun Heo /* 654def4e38dSTejun Heo * Note, unlike /proc/partitions, I am showing the 655def4e38dSTejun Heo * numbers in hex - the same format as the root= 656def4e38dSTejun Heo * option takes. 657def4e38dSTejun Heo */ 658074a7acaSTejun Heo disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0); 659074a7acaSTejun Heo while ((part = disk_part_iter_next(&piter))) { 660074a7acaSTejun Heo bool is_part0 = part == &disk->part0; 661074a7acaSTejun Heo 662074a7acaSTejun Heo printk("%s%s %10llu %s", is_part0 ? "" : " ", 663074a7acaSTejun Heo bdevt_str(part_devt(part), devt_buf), 664074a7acaSTejun Heo (unsigned long long)part->nr_sects >> 1, 665074a7acaSTejun Heo disk_name(disk, part->partno, name_buf)); 666074a7acaSTejun Heo if (is_part0) { 667def4e38dSTejun Heo if (disk->driverfs_dev != NULL && 668def4e38dSTejun Heo disk->driverfs_dev->driver != NULL) 669def4e38dSTejun Heo printk(" driver: %s\n", 670def4e38dSTejun Heo disk->driverfs_dev->driver->name); 671def4e38dSTejun Heo else 672def4e38dSTejun Heo printk(" (driver?)\n"); 673074a7acaSTejun Heo } else 674074a7acaSTejun Heo printk("\n"); 675074a7acaSTejun Heo } 676e71bf0d0STejun Heo disk_part_iter_exit(&piter); 677def4e38dSTejun Heo } 678def4e38dSTejun Heo class_dev_iter_exit(&iter); 679dd2a345fSDave Gilbert } 680dd2a345fSDave Gilbert 6811da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 6821da177e4SLinus Torvalds /* iterator */ 683def4e38dSTejun Heo static void *disk_seqf_start(struct seq_file *seqf, loff_t *pos) 68468c4d4a7SGreg Kroah-Hartman { 685def4e38dSTejun Heo loff_t skip = *pos; 686def4e38dSTejun Heo struct class_dev_iter *iter; 687def4e38dSTejun Heo struct device *dev; 68868c4d4a7SGreg Kroah-Hartman 689aeb3d3a8SHarvey Harrison iter = kmalloc(sizeof(*iter), GFP_KERNEL); 690def4e38dSTejun Heo if (!iter) 691def4e38dSTejun Heo return ERR_PTR(-ENOMEM); 692def4e38dSTejun Heo 693def4e38dSTejun Heo seqf->private = iter; 694def4e38dSTejun Heo class_dev_iter_init(iter, &block_class, NULL, &disk_type); 695def4e38dSTejun Heo do { 696def4e38dSTejun Heo dev = class_dev_iter_next(iter); 697def4e38dSTejun Heo if (!dev) 698def4e38dSTejun Heo return NULL; 699def4e38dSTejun Heo } while (skip--); 700def4e38dSTejun Heo 701def4e38dSTejun Heo return dev_to_disk(dev); 70268c4d4a7SGreg Kroah-Hartman } 70368c4d4a7SGreg Kroah-Hartman 704def4e38dSTejun Heo static void *disk_seqf_next(struct seq_file *seqf, void *v, loff_t *pos) 7051da177e4SLinus Torvalds { 706edfaa7c3SKay Sievers struct device *dev; 70766c64afeSGreg Kroah-Hartman 708def4e38dSTejun Heo (*pos)++; 709def4e38dSTejun Heo dev = class_dev_iter_next(seqf->private); 7102ac3cee5STejun Heo if (dev) 711edfaa7c3SKay Sievers return dev_to_disk(dev); 7122ac3cee5STejun Heo 7131da177e4SLinus Torvalds return NULL; 7141da177e4SLinus Torvalds } 7151da177e4SLinus Torvalds 716def4e38dSTejun Heo static void disk_seqf_stop(struct seq_file *seqf, void *v) 71727f30251SGreg Kroah-Hartman { 718def4e38dSTejun Heo struct class_dev_iter *iter = seqf->private; 719def4e38dSTejun Heo 720def4e38dSTejun Heo /* stop is called even after start failed :-( */ 721def4e38dSTejun Heo if (iter) { 722def4e38dSTejun Heo class_dev_iter_exit(iter); 723def4e38dSTejun Heo kfree(iter); 724def4e38dSTejun Heo } 72527f30251SGreg Kroah-Hartman } 72627f30251SGreg Kroah-Hartman 727def4e38dSTejun Heo static void *show_partition_start(struct seq_file *seqf, loff_t *pos) 7281da177e4SLinus Torvalds { 729def4e38dSTejun Heo static void *p; 7301da177e4SLinus Torvalds 731def4e38dSTejun Heo p = disk_seqf_start(seqf, pos); 732243294daSTejun Heo if (!IS_ERR(p) && p && !*pos) 733def4e38dSTejun Heo seq_puts(seqf, "major minor #blocks name\n\n"); 734def4e38dSTejun Heo return p; 7351da177e4SLinus Torvalds } 7361da177e4SLinus Torvalds 737cf771cb5STejun Heo static int show_partition(struct seq_file *seqf, void *v) 7381da177e4SLinus Torvalds { 7391da177e4SLinus Torvalds struct gendisk *sgp = v; 740e71bf0d0STejun Heo struct disk_part_iter piter; 741e71bf0d0STejun Heo struct hd_struct *part; 7421da177e4SLinus Torvalds char buf[BDEVNAME_SIZE]; 7431da177e4SLinus Torvalds 7441da177e4SLinus Torvalds /* Don't show non-partitionable removeable devices or empty devices */ 745b5d0b9dfSTejun Heo if (!get_capacity(sgp) || (!disk_partitionable(sgp) && 746f331c029STejun Heo (sgp->flags & GENHD_FL_REMOVABLE))) 7471da177e4SLinus Torvalds return 0; 7481da177e4SLinus Torvalds if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO) 7491da177e4SLinus Torvalds return 0; 7501da177e4SLinus Torvalds 7511da177e4SLinus Torvalds /* show the full disk and all non-0 size partitions of it */ 752074a7acaSTejun Heo disk_part_iter_init(&piter, sgp, DISK_PITER_INCL_PART0); 753e71bf0d0STejun Heo while ((part = disk_part_iter_next(&piter))) 7541f014290STejun Heo seq_printf(seqf, "%4d %7d %10llu %s\n", 755f331c029STejun Heo MAJOR(part_devt(part)), MINOR(part_devt(part)), 756f331c029STejun Heo (unsigned long long)part->nr_sects >> 1, 757f331c029STejun Heo disk_name(sgp, part->partno, buf)); 758e71bf0d0STejun Heo disk_part_iter_exit(&piter); 7591da177e4SLinus Torvalds 7601da177e4SLinus Torvalds return 0; 7611da177e4SLinus Torvalds } 7621da177e4SLinus Torvalds 763f500975aSAlexey Dobriyan static const struct seq_operations partitions_op = { 764def4e38dSTejun Heo .start = show_partition_start, 765def4e38dSTejun Heo .next = disk_seqf_next, 766def4e38dSTejun Heo .stop = disk_seqf_stop, 7671da177e4SLinus Torvalds .show = show_partition 7681da177e4SLinus Torvalds }; 769f500975aSAlexey Dobriyan 770f500975aSAlexey Dobriyan static int partitions_open(struct inode *inode, struct file *file) 771f500975aSAlexey Dobriyan { 772f500975aSAlexey Dobriyan return seq_open(file, &partitions_op); 773f500975aSAlexey Dobriyan } 774f500975aSAlexey Dobriyan 775f500975aSAlexey Dobriyan static const struct file_operations proc_partitions_operations = { 776f500975aSAlexey Dobriyan .open = partitions_open, 777f500975aSAlexey Dobriyan .read = seq_read, 778f500975aSAlexey Dobriyan .llseek = seq_lseek, 779f500975aSAlexey Dobriyan .release = seq_release, 780f500975aSAlexey Dobriyan }; 7811da177e4SLinus Torvalds #endif 7821da177e4SLinus Torvalds 7831da177e4SLinus Torvalds 784cf771cb5STejun Heo static struct kobject *base_probe(dev_t devt, int *partno, void *data) 7851da177e4SLinus Torvalds { 786edfaa7c3SKay Sievers if (request_module("block-major-%d-%d", MAJOR(devt), MINOR(devt)) > 0) 7871da177e4SLinus Torvalds /* Make old-style 2.4 aliases work */ 788edfaa7c3SKay Sievers request_module("block-major-%d", MAJOR(devt)); 7891da177e4SLinus Torvalds return NULL; 7901da177e4SLinus Torvalds } 7911da177e4SLinus Torvalds 7921da177e4SLinus Torvalds static int __init genhd_device_init(void) 7931da177e4SLinus Torvalds { 794e105b8bfSDan Williams int error; 795e105b8bfSDan Williams 796e105b8bfSDan Williams block_class.dev_kobj = sysfs_dev_block_kobj; 797e105b8bfSDan Williams error = class_register(&block_class); 798ee27a558SRoland McGrath if (unlikely(error)) 799ee27a558SRoland McGrath return error; 800edfaa7c3SKay Sievers bdev_map = kobj_map_init(base_probe, &block_class_lock); 8011da177e4SLinus Torvalds blk_dev_init(); 802edfaa7c3SKay Sievers 803561ec68eSZhang, Yanmin register_blkdev(BLOCK_EXT_MAJOR, "blkext"); 804561ec68eSZhang, Yanmin 805edfaa7c3SKay Sievers #ifndef CONFIG_SYSFS_DEPRECATED 806edfaa7c3SKay Sievers /* create top-level block dir */ 807edfaa7c3SKay Sievers block_depr = kobject_create_and_add("block", NULL); 808edfaa7c3SKay Sievers #endif 809830d3cfbSGreg Kroah-Hartman return 0; 8101da177e4SLinus Torvalds } 8111da177e4SLinus Torvalds 8121da177e4SLinus Torvalds subsys_initcall(genhd_device_init); 8131da177e4SLinus Torvalds 814edfaa7c3SKay Sievers static ssize_t disk_range_show(struct device *dev, 815edfaa7c3SKay Sievers struct device_attribute *attr, char *buf) 8161da177e4SLinus Torvalds { 817edfaa7c3SKay Sievers struct gendisk *disk = dev_to_disk(dev); 8181da177e4SLinus Torvalds 819edfaa7c3SKay Sievers return sprintf(buf, "%d\n", disk->minors); 8201da177e4SLinus Torvalds } 8211da177e4SLinus Torvalds 8221f014290STejun Heo static ssize_t disk_ext_range_show(struct device *dev, 8231f014290STejun Heo struct device_attribute *attr, char *buf) 8241f014290STejun Heo { 8251f014290STejun Heo struct gendisk *disk = dev_to_disk(dev); 8261f014290STejun Heo 827b5d0b9dfSTejun Heo return sprintf(buf, "%d\n", disk_max_parts(disk)); 8281f014290STejun Heo } 8291f014290STejun Heo 830edfaa7c3SKay Sievers static ssize_t disk_removable_show(struct device *dev, 831edfaa7c3SKay Sievers struct device_attribute *attr, char *buf) 832a7fd6706SKay Sievers { 833edfaa7c3SKay Sievers struct gendisk *disk = dev_to_disk(dev); 834a7fd6706SKay Sievers 835edfaa7c3SKay Sievers return sprintf(buf, "%d\n", 8361da177e4SLinus Torvalds (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0)); 837edfaa7c3SKay Sievers } 8381da177e4SLinus Torvalds 8391c9ce527SKay Sievers static ssize_t disk_ro_show(struct device *dev, 8401c9ce527SKay Sievers struct device_attribute *attr, char *buf) 8411c9ce527SKay Sievers { 8421c9ce527SKay Sievers struct gendisk *disk = dev_to_disk(dev); 8431c9ce527SKay Sievers 844b7db9956STejun Heo return sprintf(buf, "%d\n", get_disk_ro(disk) ? 1 : 0); 8451c9ce527SKay Sievers } 8461c9ce527SKay Sievers 847edfaa7c3SKay Sievers static ssize_t disk_capability_show(struct device *dev, 848edfaa7c3SKay Sievers struct device_attribute *attr, char *buf) 84986ce18d7SKristen Carlson Accardi { 850edfaa7c3SKay Sievers struct gendisk *disk = dev_to_disk(dev); 851edfaa7c3SKay Sievers 852edfaa7c3SKay Sievers return sprintf(buf, "%x\n", disk->flags); 85386ce18d7SKristen Carlson Accardi } 854edfaa7c3SKay Sievers 855c72758f3SMartin K. Petersen static ssize_t disk_alignment_offset_show(struct device *dev, 856c72758f3SMartin K. Petersen struct device_attribute *attr, 857c72758f3SMartin K. Petersen char *buf) 858c72758f3SMartin K. Petersen { 859c72758f3SMartin K. Petersen struct gendisk *disk = dev_to_disk(dev); 860c72758f3SMartin K. Petersen 861c72758f3SMartin K. Petersen return sprintf(buf, "%d\n", queue_alignment_offset(disk->queue)); 862c72758f3SMartin K. Petersen } 863c72758f3SMartin K. Petersen 864edfaa7c3SKay Sievers static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); 8651f014290STejun Heo static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL); 866edfaa7c3SKay Sievers static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); 8671c9ce527SKay Sievers static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL); 868e5610521STejun Heo static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL); 869c72758f3SMartin K. Petersen static DEVICE_ATTR(alignment_offset, S_IRUGO, disk_alignment_offset_show, NULL); 870edfaa7c3SKay Sievers static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL); 871074a7acaSTejun Heo static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL); 872*316d315bSNikanth Karthikesan static DEVICE_ATTR(inflight, S_IRUGO, part_inflight_show, NULL); 873c17bb495SAkinobu Mita #ifdef CONFIG_FAIL_MAKE_REQUEST 874edfaa7c3SKay Sievers static struct device_attribute dev_attr_fail = 875eddb2e26STejun Heo __ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store); 876c17bb495SAkinobu Mita #endif 877581d4e28SJens Axboe #ifdef CONFIG_FAIL_IO_TIMEOUT 878581d4e28SJens Axboe static struct device_attribute dev_attr_fail_timeout = 879581d4e28SJens Axboe __ATTR(io-timeout-fail, S_IRUGO|S_IWUSR, part_timeout_show, 880581d4e28SJens Axboe part_timeout_store); 881581d4e28SJens Axboe #endif 882edfaa7c3SKay Sievers 883edfaa7c3SKay Sievers static struct attribute *disk_attrs[] = { 884edfaa7c3SKay Sievers &dev_attr_range.attr, 8851f014290STejun Heo &dev_attr_ext_range.attr, 886edfaa7c3SKay Sievers &dev_attr_removable.attr, 8871c9ce527SKay Sievers &dev_attr_ro.attr, 888edfaa7c3SKay Sievers &dev_attr_size.attr, 889c72758f3SMartin K. Petersen &dev_attr_alignment_offset.attr, 890edfaa7c3SKay Sievers &dev_attr_capability.attr, 891edfaa7c3SKay Sievers &dev_attr_stat.attr, 892*316d315bSNikanth Karthikesan &dev_attr_inflight.attr, 893edfaa7c3SKay Sievers #ifdef CONFIG_FAIL_MAKE_REQUEST 894edfaa7c3SKay Sievers &dev_attr_fail.attr, 895edfaa7c3SKay Sievers #endif 896581d4e28SJens Axboe #ifdef CONFIG_FAIL_IO_TIMEOUT 897581d4e28SJens Axboe &dev_attr_fail_timeout.attr, 898581d4e28SJens Axboe #endif 899edfaa7c3SKay Sievers NULL 9001da177e4SLinus Torvalds }; 9011da177e4SLinus Torvalds 902edfaa7c3SKay Sievers static struct attribute_group disk_attr_group = { 903edfaa7c3SKay Sievers .attrs = disk_attrs, 904edfaa7c3SKay Sievers }; 905edfaa7c3SKay Sievers 906a4dbd674SDavid Brownell static const struct attribute_group *disk_attr_groups[] = { 907edfaa7c3SKay Sievers &disk_attr_group, 908edfaa7c3SKay Sievers NULL 909edfaa7c3SKay Sievers }; 910edfaa7c3SKay Sievers 911540eed56STejun Heo static void disk_free_ptbl_rcu_cb(struct rcu_head *head) 912540eed56STejun Heo { 913540eed56STejun Heo struct disk_part_tbl *ptbl = 914540eed56STejun Heo container_of(head, struct disk_part_tbl, rcu_head); 915540eed56STejun Heo 916540eed56STejun Heo kfree(ptbl); 917540eed56STejun Heo } 918540eed56STejun Heo 919540eed56STejun Heo /** 920540eed56STejun Heo * disk_replace_part_tbl - replace disk->part_tbl in RCU-safe way 921540eed56STejun Heo * @disk: disk to replace part_tbl for 922540eed56STejun Heo * @new_ptbl: new part_tbl to install 923540eed56STejun Heo * 924540eed56STejun Heo * Replace disk->part_tbl with @new_ptbl in RCU-safe way. The 925540eed56STejun Heo * original ptbl is freed using RCU callback. 926540eed56STejun Heo * 927540eed56STejun Heo * LOCKING: 928540eed56STejun Heo * Matching bd_mutx locked. 929540eed56STejun Heo */ 930540eed56STejun Heo static void disk_replace_part_tbl(struct gendisk *disk, 931540eed56STejun Heo struct disk_part_tbl *new_ptbl) 932540eed56STejun Heo { 933540eed56STejun Heo struct disk_part_tbl *old_ptbl = disk->part_tbl; 934540eed56STejun Heo 935540eed56STejun Heo rcu_assign_pointer(disk->part_tbl, new_ptbl); 936a6f23657SJens Axboe 937a6f23657SJens Axboe if (old_ptbl) { 938a6f23657SJens Axboe rcu_assign_pointer(old_ptbl->last_lookup, NULL); 939540eed56STejun Heo call_rcu(&old_ptbl->rcu_head, disk_free_ptbl_rcu_cb); 940540eed56STejun Heo } 941a6f23657SJens Axboe } 942540eed56STejun Heo 943540eed56STejun Heo /** 944540eed56STejun Heo * disk_expand_part_tbl - expand disk->part_tbl 945540eed56STejun Heo * @disk: disk to expand part_tbl for 946540eed56STejun Heo * @partno: expand such that this partno can fit in 947540eed56STejun Heo * 948540eed56STejun Heo * Expand disk->part_tbl such that @partno can fit in. disk->part_tbl 949540eed56STejun Heo * uses RCU to allow unlocked dereferencing for stats and other stuff. 950540eed56STejun Heo * 951540eed56STejun Heo * LOCKING: 952540eed56STejun Heo * Matching bd_mutex locked, might sleep. 953540eed56STejun Heo * 954540eed56STejun Heo * RETURNS: 955540eed56STejun Heo * 0 on success, -errno on failure. 956540eed56STejun Heo */ 957540eed56STejun Heo int disk_expand_part_tbl(struct gendisk *disk, int partno) 958540eed56STejun Heo { 959540eed56STejun Heo struct disk_part_tbl *old_ptbl = disk->part_tbl; 960540eed56STejun Heo struct disk_part_tbl *new_ptbl; 961540eed56STejun Heo int len = old_ptbl ? old_ptbl->len : 0; 962540eed56STejun Heo int target = partno + 1; 963540eed56STejun Heo size_t size; 964540eed56STejun Heo int i; 965540eed56STejun Heo 966540eed56STejun Heo /* disk_max_parts() is zero during initialization, ignore if so */ 967540eed56STejun Heo if (disk_max_parts(disk) && target > disk_max_parts(disk)) 968540eed56STejun Heo return -EINVAL; 969540eed56STejun Heo 970540eed56STejun Heo if (target <= len) 971540eed56STejun Heo return 0; 972540eed56STejun Heo 973540eed56STejun Heo size = sizeof(*new_ptbl) + target * sizeof(new_ptbl->part[0]); 974540eed56STejun Heo new_ptbl = kzalloc_node(size, GFP_KERNEL, disk->node_id); 975540eed56STejun Heo if (!new_ptbl) 976540eed56STejun Heo return -ENOMEM; 977540eed56STejun Heo 978540eed56STejun Heo INIT_RCU_HEAD(&new_ptbl->rcu_head); 979540eed56STejun Heo new_ptbl->len = target; 980540eed56STejun Heo 981540eed56STejun Heo for (i = 0; i < len; i++) 982540eed56STejun Heo rcu_assign_pointer(new_ptbl->part[i], old_ptbl->part[i]); 983540eed56STejun Heo 984540eed56STejun Heo disk_replace_part_tbl(disk, new_ptbl); 985540eed56STejun Heo return 0; 986540eed56STejun Heo } 987540eed56STejun Heo 988edfaa7c3SKay Sievers static void disk_release(struct device *dev) 9891da177e4SLinus Torvalds { 990edfaa7c3SKay Sievers struct gendisk *disk = dev_to_disk(dev); 991edfaa7c3SKay Sievers 9921da177e4SLinus Torvalds kfree(disk->random); 993540eed56STejun Heo disk_replace_part_tbl(disk, NULL); 994074a7acaSTejun Heo free_part_stats(&disk->part0); 9951da177e4SLinus Torvalds kfree(disk); 9961da177e4SLinus Torvalds } 997edfaa7c3SKay Sievers struct class block_class = { 998edfaa7c3SKay Sievers .name = "block", 9991da177e4SLinus Torvalds }; 10001da177e4SLinus Torvalds 1001e454cea2SKay Sievers static char *block_devnode(struct device *dev, mode_t *mode) 1002b03f38b6SKay Sievers { 1003b03f38b6SKay Sievers struct gendisk *disk = dev_to_disk(dev); 1004b03f38b6SKay Sievers 1005e454cea2SKay Sievers if (disk->devnode) 1006e454cea2SKay Sievers return disk->devnode(disk, mode); 1007b03f38b6SKay Sievers return NULL; 1008b03f38b6SKay Sievers } 1009b03f38b6SKay Sievers 10101826eadfSAdrian Bunk static struct device_type disk_type = { 1011edfaa7c3SKay Sievers .name = "disk", 1012edfaa7c3SKay Sievers .groups = disk_attr_groups, 1013edfaa7c3SKay Sievers .release = disk_release, 1014e454cea2SKay Sievers .devnode = block_devnode, 10151da177e4SLinus Torvalds }; 10161da177e4SLinus Torvalds 1017a6e2ba88SRandy Dunlap #ifdef CONFIG_PROC_FS 1018cf771cb5STejun Heo /* 1019cf771cb5STejun Heo * aggregate disk stat collector. Uses the same stats that the sysfs 1020cf771cb5STejun Heo * entries do, above, but makes them available through one seq_file. 1021cf771cb5STejun Heo * 1022cf771cb5STejun Heo * The output looks suspiciously like /proc/partitions with a bunch of 1023cf771cb5STejun Heo * extra fields. 1024cf771cb5STejun Heo */ 1025cf771cb5STejun Heo static int diskstats_show(struct seq_file *seqf, void *v) 10261da177e4SLinus Torvalds { 10271da177e4SLinus Torvalds struct gendisk *gp = v; 1028e71bf0d0STejun Heo struct disk_part_iter piter; 1029e71bf0d0STejun Heo struct hd_struct *hd; 10301da177e4SLinus Torvalds char buf[BDEVNAME_SIZE]; 1031c9959059STejun Heo int cpu; 10321da177e4SLinus Torvalds 10331da177e4SLinus Torvalds /* 1034ed9e1982STejun Heo if (&disk_to_dev(gp)->kobj.entry == block_class.devices.next) 1035cf771cb5STejun Heo seq_puts(seqf, "major minor name" 10361da177e4SLinus Torvalds " rio rmerge rsect ruse wio wmerge " 10371da177e4SLinus Torvalds "wsect wuse running use aveq" 10381da177e4SLinus Torvalds "\n\n"); 10391da177e4SLinus Torvalds */ 10401da177e4SLinus Torvalds 104171982a40STejun Heo disk_part_iter_init(&piter, gp, DISK_PITER_INCL_EMPTY_PART0); 1042e71bf0d0STejun Heo while ((hd = disk_part_iter_next(&piter))) { 1043074a7acaSTejun Heo cpu = part_stat_lock(); 1044c9959059STejun Heo part_round_stats(cpu, hd); 1045074a7acaSTejun Heo part_stat_unlock(); 10461f014290STejun Heo seq_printf(seqf, "%4d %7d %s %lu %lu %llu " 104728f39d55SJerome Marchand "%u %lu %lu %llu %u %u %u %u\n", 1048f331c029STejun Heo MAJOR(part_devt(hd)), MINOR(part_devt(hd)), 1049f331c029STejun Heo disk_name(gp, hd->partno, buf), 105028f39d55SJerome Marchand part_stat_read(hd, ios[0]), 105128f39d55SJerome Marchand part_stat_read(hd, merges[0]), 105228f39d55SJerome Marchand (unsigned long long)part_stat_read(hd, sectors[0]), 105328f39d55SJerome Marchand jiffies_to_msecs(part_stat_read(hd, ticks[0])), 105428f39d55SJerome Marchand part_stat_read(hd, ios[1]), 105528f39d55SJerome Marchand part_stat_read(hd, merges[1]), 105628f39d55SJerome Marchand (unsigned long long)part_stat_read(hd, sectors[1]), 105728f39d55SJerome Marchand jiffies_to_msecs(part_stat_read(hd, ticks[1])), 1058*316d315bSNikanth Karthikesan part_in_flight(hd), 105928f39d55SJerome Marchand jiffies_to_msecs(part_stat_read(hd, io_ticks)), 106028f39d55SJerome Marchand jiffies_to_msecs(part_stat_read(hd, time_in_queue)) 106128f39d55SJerome Marchand ); 10621da177e4SLinus Torvalds } 1063e71bf0d0STejun Heo disk_part_iter_exit(&piter); 10641da177e4SLinus Torvalds 10651da177e4SLinus Torvalds return 0; 10661da177e4SLinus Torvalds } 10671da177e4SLinus Torvalds 106831d85ab2SAlexey Dobriyan static const struct seq_operations diskstats_op = { 1069def4e38dSTejun Heo .start = disk_seqf_start, 1070def4e38dSTejun Heo .next = disk_seqf_next, 1071def4e38dSTejun Heo .stop = disk_seqf_stop, 10721da177e4SLinus Torvalds .show = diskstats_show 10731da177e4SLinus Torvalds }; 1074f500975aSAlexey Dobriyan 107531d85ab2SAlexey Dobriyan static int diskstats_open(struct inode *inode, struct file *file) 107631d85ab2SAlexey Dobriyan { 107731d85ab2SAlexey Dobriyan return seq_open(file, &diskstats_op); 107831d85ab2SAlexey Dobriyan } 107931d85ab2SAlexey Dobriyan 108031d85ab2SAlexey Dobriyan static const struct file_operations proc_diskstats_operations = { 108131d85ab2SAlexey Dobriyan .open = diskstats_open, 108231d85ab2SAlexey Dobriyan .read = seq_read, 108331d85ab2SAlexey Dobriyan .llseek = seq_lseek, 108431d85ab2SAlexey Dobriyan .release = seq_release, 108531d85ab2SAlexey Dobriyan }; 108631d85ab2SAlexey Dobriyan 1087f500975aSAlexey Dobriyan static int __init proc_genhd_init(void) 1088f500975aSAlexey Dobriyan { 108931d85ab2SAlexey Dobriyan proc_create("diskstats", 0, NULL, &proc_diskstats_operations); 1090f500975aSAlexey Dobriyan proc_create("partitions", 0, NULL, &proc_partitions_operations); 1091f500975aSAlexey Dobriyan return 0; 1092f500975aSAlexey Dobriyan } 1093f500975aSAlexey Dobriyan module_init(proc_genhd_init); 1094a6e2ba88SRandy Dunlap #endif /* CONFIG_PROC_FS */ 10951da177e4SLinus Torvalds 10968ce7ad7bSKristen Carlson Accardi static void media_change_notify_thread(struct work_struct *work) 10978ce7ad7bSKristen Carlson Accardi { 10988ce7ad7bSKristen Carlson Accardi struct gendisk *gd = container_of(work, struct gendisk, async_notify); 10998ce7ad7bSKristen Carlson Accardi char event[] = "MEDIA_CHANGE=1"; 11008ce7ad7bSKristen Carlson Accardi char *envp[] = { event, NULL }; 11018ce7ad7bSKristen Carlson Accardi 11028ce7ad7bSKristen Carlson Accardi /* 11038ce7ad7bSKristen Carlson Accardi * set enviroment vars to indicate which event this is for 11048ce7ad7bSKristen Carlson Accardi * so that user space will know to go check the media status. 11058ce7ad7bSKristen Carlson Accardi */ 1106ed9e1982STejun Heo kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp); 11078ce7ad7bSKristen Carlson Accardi put_device(gd->driverfs_dev); 11088ce7ad7bSKristen Carlson Accardi } 11098ce7ad7bSKristen Carlson Accardi 11101826eadfSAdrian Bunk #if 0 11118ce7ad7bSKristen Carlson Accardi void genhd_media_change_notify(struct gendisk *disk) 11128ce7ad7bSKristen Carlson Accardi { 11138ce7ad7bSKristen Carlson Accardi get_device(disk->driverfs_dev); 11148ce7ad7bSKristen Carlson Accardi schedule_work(&disk->async_notify); 11158ce7ad7bSKristen Carlson Accardi } 11168ce7ad7bSKristen Carlson Accardi EXPORT_SYMBOL_GPL(genhd_media_change_notify); 11171826eadfSAdrian Bunk #endif /* 0 */ 11188ce7ad7bSKristen Carlson Accardi 1119cf771cb5STejun Heo dev_t blk_lookup_devt(const char *name, int partno) 1120edfaa7c3SKay Sievers { 1121edfaa7c3SKay Sievers dev_t devt = MKDEV(0, 0); 1122def4e38dSTejun Heo struct class_dev_iter iter; 1123def4e38dSTejun Heo struct device *dev; 1124edfaa7c3SKay Sievers 1125def4e38dSTejun Heo class_dev_iter_init(&iter, &block_class, NULL, &disk_type); 1126def4e38dSTejun Heo while ((dev = class_dev_iter_next(&iter))) { 1127def4e38dSTejun Heo struct gendisk *disk = dev_to_disk(dev); 1128548b10ebSTejun Heo struct hd_struct *part; 1129def4e38dSTejun Heo 11303ada8b7eSKay Sievers if (strcmp(dev_name(dev), name)) 1131f331c029STejun Heo continue; 1132f331c029STejun Heo 113341b8c853SNeil Brown if (partno < disk->minors) { 113441b8c853SNeil Brown /* We need to return the right devno, even 113541b8c853SNeil Brown * if the partition doesn't exist yet. 113641b8c853SNeil Brown */ 113741b8c853SNeil Brown devt = MKDEV(MAJOR(dev->devt), 113841b8c853SNeil Brown MINOR(dev->devt) + partno); 113941b8c853SNeil Brown break; 114041b8c853SNeil Brown } 1141e71bf0d0STejun Heo part = disk_get_part(disk, partno); 11422bbedcb4STejun Heo if (part) { 1143f331c029STejun Heo devt = part_devt(part); 1144e71bf0d0STejun Heo disk_put_part(part); 1145f331c029STejun Heo break; 1146def4e38dSTejun Heo } 1147548b10ebSTejun Heo disk_put_part(part); 1148548b10ebSTejun Heo } 1149def4e38dSTejun Heo class_dev_iter_exit(&iter); 1150edfaa7c3SKay Sievers return devt; 1151edfaa7c3SKay Sievers } 1152edfaa7c3SKay Sievers EXPORT_SYMBOL(blk_lookup_devt); 1153edfaa7c3SKay Sievers 11541da177e4SLinus Torvalds struct gendisk *alloc_disk(int minors) 11551da177e4SLinus Torvalds { 11561946089aSChristoph Lameter return alloc_disk_node(minors, -1); 11571946089aSChristoph Lameter } 1158689d6facSTejun Heo EXPORT_SYMBOL(alloc_disk); 11591946089aSChristoph Lameter 11601946089aSChristoph Lameter struct gendisk *alloc_disk_node(int minors, int node_id) 11611946089aSChristoph Lameter { 11621946089aSChristoph Lameter struct gendisk *disk; 11631946089aSChristoph Lameter 116494f6030cSChristoph Lameter disk = kmalloc_node(sizeof(struct gendisk), 116594f6030cSChristoph Lameter GFP_KERNEL | __GFP_ZERO, node_id); 11661da177e4SLinus Torvalds if (disk) { 1167074a7acaSTejun Heo if (!init_part_stats(&disk->part0)) { 11681da177e4SLinus Torvalds kfree(disk); 11691da177e4SLinus Torvalds return NULL; 11701da177e4SLinus Torvalds } 1171bf91db18SCheng Renquan disk->node_id = node_id; 1172540eed56STejun Heo if (disk_expand_part_tbl(disk, 0)) { 1173074a7acaSTejun Heo free_part_stats(&disk->part0); 11741da177e4SLinus Torvalds kfree(disk); 11751da177e4SLinus Torvalds return NULL; 11761da177e4SLinus Torvalds } 1177540eed56STejun Heo disk->part_tbl->part[0] = &disk->part0; 1178b5d0b9dfSTejun Heo 11791da177e4SLinus Torvalds disk->minors = minors; 11801da177e4SLinus Torvalds rand_initialize_disk(disk); 1181ed9e1982STejun Heo disk_to_dev(disk)->class = &block_class; 1182ed9e1982STejun Heo disk_to_dev(disk)->type = &disk_type; 1183ed9e1982STejun Heo device_initialize(disk_to_dev(disk)); 11848ce7ad7bSKristen Carlson Accardi INIT_WORK(&disk->async_notify, 11858ce7ad7bSKristen Carlson Accardi media_change_notify_thread); 11861da177e4SLinus Torvalds } 11871da177e4SLinus Torvalds return disk; 11881da177e4SLinus Torvalds } 11891946089aSChristoph Lameter EXPORT_SYMBOL(alloc_disk_node); 11901da177e4SLinus Torvalds 11911da177e4SLinus Torvalds struct kobject *get_disk(struct gendisk *disk) 11921da177e4SLinus Torvalds { 11931da177e4SLinus Torvalds struct module *owner; 11941da177e4SLinus Torvalds struct kobject *kobj; 11951da177e4SLinus Torvalds 11961da177e4SLinus Torvalds if (!disk->fops) 11971da177e4SLinus Torvalds return NULL; 11981da177e4SLinus Torvalds owner = disk->fops->owner; 11991da177e4SLinus Torvalds if (owner && !try_module_get(owner)) 12001da177e4SLinus Torvalds return NULL; 1201ed9e1982STejun Heo kobj = kobject_get(&disk_to_dev(disk)->kobj); 12021da177e4SLinus Torvalds if (kobj == NULL) { 12031da177e4SLinus Torvalds module_put(owner); 12041da177e4SLinus Torvalds return NULL; 12051da177e4SLinus Torvalds } 12061da177e4SLinus Torvalds return kobj; 12071da177e4SLinus Torvalds 12081da177e4SLinus Torvalds } 12091da177e4SLinus Torvalds 12101da177e4SLinus Torvalds EXPORT_SYMBOL(get_disk); 12111da177e4SLinus Torvalds 12121da177e4SLinus Torvalds void put_disk(struct gendisk *disk) 12131da177e4SLinus Torvalds { 12141da177e4SLinus Torvalds if (disk) 1215ed9e1982STejun Heo kobject_put(&disk_to_dev(disk)->kobj); 12161da177e4SLinus Torvalds } 12171da177e4SLinus Torvalds 12181da177e4SLinus Torvalds EXPORT_SYMBOL(put_disk); 12191da177e4SLinus Torvalds 1220e3264a4dSHannes Reinecke static void set_disk_ro_uevent(struct gendisk *gd, int ro) 1221e3264a4dSHannes Reinecke { 1222e3264a4dSHannes Reinecke char event[] = "DISK_RO=1"; 1223e3264a4dSHannes Reinecke char *envp[] = { event, NULL }; 1224e3264a4dSHannes Reinecke 1225e3264a4dSHannes Reinecke if (!ro) 1226e3264a4dSHannes Reinecke event[8] = '0'; 1227e3264a4dSHannes Reinecke kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp); 1228e3264a4dSHannes Reinecke } 1229e3264a4dSHannes Reinecke 12301da177e4SLinus Torvalds void set_device_ro(struct block_device *bdev, int flag) 12311da177e4SLinus Torvalds { 12321da177e4SLinus Torvalds bdev->bd_part->policy = flag; 12331da177e4SLinus Torvalds } 12341da177e4SLinus Torvalds 12351da177e4SLinus Torvalds EXPORT_SYMBOL(set_device_ro); 12361da177e4SLinus Torvalds 12371da177e4SLinus Torvalds void set_disk_ro(struct gendisk *disk, int flag) 12381da177e4SLinus Torvalds { 1239e71bf0d0STejun Heo struct disk_part_iter piter; 1240e71bf0d0STejun Heo struct hd_struct *part; 1241e71bf0d0STejun Heo 1242e3264a4dSHannes Reinecke if (disk->part0.policy != flag) { 1243e3264a4dSHannes Reinecke set_disk_ro_uevent(disk, flag); 1244e3264a4dSHannes Reinecke disk->part0.policy = flag; 1245e3264a4dSHannes Reinecke } 1246e3264a4dSHannes Reinecke 1247e3264a4dSHannes Reinecke disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY); 1248e71bf0d0STejun Heo while ((part = disk_part_iter_next(&piter))) 1249e71bf0d0STejun Heo part->policy = flag; 1250e71bf0d0STejun Heo disk_part_iter_exit(&piter); 12511da177e4SLinus Torvalds } 12521da177e4SLinus Torvalds 12531da177e4SLinus Torvalds EXPORT_SYMBOL(set_disk_ro); 12541da177e4SLinus Torvalds 12551da177e4SLinus Torvalds int bdev_read_only(struct block_device *bdev) 12561da177e4SLinus Torvalds { 12571da177e4SLinus Torvalds if (!bdev) 12581da177e4SLinus Torvalds return 0; 12591da177e4SLinus Torvalds return bdev->bd_part->policy; 12601da177e4SLinus Torvalds } 12611da177e4SLinus Torvalds 12621da177e4SLinus Torvalds EXPORT_SYMBOL(bdev_read_only); 12631da177e4SLinus Torvalds 1264cf771cb5STejun Heo int invalidate_partition(struct gendisk *disk, int partno) 12651da177e4SLinus Torvalds { 12661da177e4SLinus Torvalds int res = 0; 1267cf771cb5STejun Heo struct block_device *bdev = bdget_disk(disk, partno); 12681da177e4SLinus Torvalds if (bdev) { 12692ef41634SChristoph Hellwig fsync_bdev(bdev); 12702ef41634SChristoph Hellwig res = __invalidate_device(bdev); 12711da177e4SLinus Torvalds bdput(bdev); 12721da177e4SLinus Torvalds } 12731da177e4SLinus Torvalds return res; 12741da177e4SLinus Torvalds } 12751da177e4SLinus Torvalds 12761da177e4SLinus Torvalds EXPORT_SYMBOL(invalidate_partition); 1277