/* * Code extracted from * linux/kernel/hd.c * * Copyright (C) 1991-1998 Linus Torvalds * * devfs support - jj, rgooch, 980122 * * Moved partition checking code to fs/partitions* - Russell King * (linux@arm.uk.linux.org) */ /* * TODO: rip out the remaining init crap from this file --hch */ #include <linux/config.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/genhd.h> #include <linux/kernel.h> #include <linux/blk.h> #include <linux/init.h> #include <linux/spinlock.h> #include <linux/seq_file.h> #include <linux/slab.h> static rwlock_t gendisk_lock; /* * Global kernel list of partitioning information. */ static LIST_HEAD(gendisk_list); /* * TEMPORARY KLUDGE. */ static struct { struct list_head list; struct gendisk *(*get)(int minor); } gendisks[MAX_BLKDEV]; void blk_set_probe(int major, struct gendisk *(p)(int)) { write_lock(&gendisk_lock); gendisks[major].get = p; write_unlock(&gendisk_lock); } EXPORT_SYMBOL(blk_set_probe); /* Will go away */ /** * add_gendisk - add partitioning information to kernel list * @gp: per-device partitioning information * * This function registers the partitioning information in @gp * with the kernel. */ void add_disk(struct gendisk *disk) { write_lock(&gendisk_lock); list_add(&disk->list, &gendisks[disk->major].list); if (disk->minors > 1) list_add_tail(&disk->full_list, &gendisk_list); else INIT_LIST_HEAD(&disk->full_list); write_unlock(&gendisk_lock); disk->flags |= GENHD_FL_UP; register_disk(disk); } EXPORT_SYMBOL(add_disk); EXPORT_SYMBOL(del_gendisk); void unlink_gendisk(struct gendisk *disk) { write_lock(&gendisk_lock); list_del_init(&disk->full_list); list_del_init(&disk->list); write_unlock(&gendisk_lock); } /** * get_gendisk - get partitioning information for a given device * @dev: device to get partitioning information for * * This function gets the structure containing partitioning * information for the given device @dev. */ struct gendisk * get_gendisk(dev_t dev, int *part) { struct gendisk *disk; struct list_head *p; int major = MAJOR(dev); int minor = MINOR(dev); *part = 0; read_lock(&gendisk_lock); if (gendisks[major].get) { disk = gendisks[major].get(minor); if (disk) get_disk(disk); read_unlock(&gendisk_lock); return disk; } list_for_each(p, &gendisks[major].list) { disk = list_entry(p, struct gendisk, list); if (disk->first_minor > minor) continue; if (disk->first_minor + disk->minors <= minor) continue; get_disk(disk); read_unlock(&gendisk_lock); *part = minor - disk->first_minor; return disk; } read_unlock(&gendisk_lock); return NULL; } EXPORT_SYMBOL(get_gendisk); #ifdef CONFIG_PROC_FS /* iterator */ static void *part_start(struct seq_file *part, loff_t *pos) { struct list_head *p; loff_t l = *pos; read_lock(&gendisk_lock); list_for_each(p, &gendisk_list) if (!l--) return list_entry(p, struct gendisk, full_list); return NULL; } static void *part_next(struct seq_file *part, void *v, loff_t *pos) { struct list_head *p = ((struct gendisk *)v)->full_list.next; ++*pos; return p==&gendisk_list ? NULL : list_entry(p, struct gendisk, full_list); } static void part_stop(struct seq_file *part, void *v) { read_unlock(&gendisk_lock); } static int show_partition(struct seq_file *part, void *v) { struct gendisk *sgp = v; int n; char buf[64]; if (&sgp->full_list == gendisk_list.next) seq_puts(part, "major minor #blocks name\n\n"); /* Don't show non-partitionable devices or empty devices */ if (!get_capacity(sgp)) return 0; /* show the full disk and all non-0 size partitions of it */ seq_printf(part, "%4d %4d %10llu %s\n", sgp->major, sgp->first_minor, (unsigned long long)get_capacity(sgp) >> 1, disk_name(sgp, 0, buf)); for (n = 0; n < sgp->minors - 1; n++) { if (sgp->part[n].nr_sects == 0) continue; seq_printf(part, "%4d %4d %10llu %s\n", sgp->major, n + 1 + sgp->first_minor, (unsigned long long)sgp->part[n].nr_sects >> 1 , disk_name(sgp, n + 1, buf)); } return 0; } struct seq_operations partitions_op = { start: part_start, next: part_next, stop: part_stop, show: show_partition }; #endif extern int blk_dev_init(void); extern int soc_probe(void); extern int atmdev_init(void); struct device_class disk_devclass = { .name = "disk", }; static struct bus_type disk_bus = { name: "block", }; int __init device_init(void) { int i; rwlock_init(&gendisk_lock); for (i = 0; i < MAX_BLKDEV; i++) INIT_LIST_HEAD(&gendisks[i].list); blk_dev_init(); devclass_register(&disk_devclass); bus_register(&disk_bus); return 0; } __initcall(device_init); EXPORT_SYMBOL(disk_devclass); static void disk_release(struct device *dev) { struct gendisk *disk = dev->driver_data; kfree(disk->part); kfree(disk); } struct gendisk *alloc_disk(int minors) { struct gendisk *disk = kmalloc(sizeof(struct gendisk), GFP_KERNEL); if (disk) { memset(disk, 0, sizeof(struct gendisk)); if (minors > 1) { int size = (minors - 1) * sizeof(struct hd_struct); disk->part = kmalloc(size, GFP_KERNEL); if (!disk->part) { kfree(disk); return NULL; } memset(disk->part, 0, size); } disk->minors = minors; while (minors >>= 1) disk->minor_shift++; disk->disk_dev.bus = &disk_bus; disk->disk_dev.release = disk_release; disk->disk_dev.driver_data = disk; device_initialize(&disk->disk_dev); } return disk; } struct gendisk *get_disk(struct gendisk *disk) { atomic_inc(&disk->disk_dev.refcount); return disk; } void put_disk(struct gendisk *disk) { if (disk) put_device(&disk->disk_dev); } EXPORT_SYMBOL(alloc_disk); EXPORT_SYMBOL(get_disk); EXPORT_SYMBOL(put_disk);