/* * 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 #include #include #include #include #include #include #include #include /* * Global kernel list of partitioning information. * * XXX: you should _never_ access this directly. * the only reason this is exported is source compatiblity. */ /*static*/ struct gendisk *gendisk_head; static struct gendisk *gendisk_array[MAX_BLKDEV]; static rwlock_t gendisk_lock = RW_LOCK_UNLOCKED; EXPORT_SYMBOL(gendisk_head); /** * 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_gendisk(struct gendisk *gp) { struct gendisk *sgp; unsigned long flags; write_lock_irqsave(&gendisk_lock, flags); /* * In 2.5 this will go away. Fix the drivers who rely on * old behaviour. */ for (sgp = gendisk_head; sgp; sgp = sgp->next) { if (sgp == gp) { // printk(KERN_ERR "add_gendisk: device major %d is buggy and added a live gendisk!\n", // sgp->major) goto out; } } gendisk_array[gp->major] = gp; gp->next = gendisk_head; gendisk_head = gp; out: write_unlock_irqrestore(&gendisk_lock, flags); } EXPORT_SYMBOL(add_gendisk); /** * del_gendisk - remove partitioning information from kernel list * @gp: per-device partitioning information * * This function unregisters the partitioning information in @gp * with the kernel. */ void del_gendisk(struct gendisk *gp) { struct gendisk **gpp; unsigned long flags; write_lock_irqsave(&gendisk_lock, flags); gendisk_array[gp->major] = NULL; for (gpp = &gendisk_head; *gpp; gpp = &((*gpp)->next)) if (*gpp == gp) break; if (*gpp) *gpp = (*gpp)->next; write_unlock_irqrestore(&gendisk_lock, flags); } EXPORT_SYMBOL(del_gendisk); /** * 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(kdev_t dev) { struct gendisk *gp = NULL; int maj = MAJOR(dev); unsigned long flags; read_lock_irqsave(&gendisk_lock, flags); if ((gp = gendisk_array[maj])) goto out; /* This is needed for early 2.4 source compatiblity. --hch */ for (gp = gendisk_head; gp; gp = gp->next) if (gp->major == maj) break; out: read_unlock_irqrestore(&gendisk_lock, flags); return gp; } EXPORT_SYMBOL(get_gendisk); /** * walk_gendisk - issue a command for every registered gendisk * @walk: user-specified callback * @data: opaque data for the callback * * This function walks through the gendisk chain and calls back * into @walk for every element. */ int walk_gendisk(int (*walk)(struct gendisk *, void *), void *data) { struct gendisk *gp; int error = 0; unsigned long flags; read_lock_irqsave(&gendisk_lock, flags); for (gp = gendisk_head; gp; gp = gp->next) if ((error = walk(gp, data))) break; read_unlock_irqrestore(&gendisk_lock, flags); return error; } #ifdef CONFIG_PROC_FS /* iterator */ static void *part_start(struct seq_file *s, loff_t *ppos) { struct gendisk *gp; loff_t pos = *ppos; read_lock_irq(&gendisk_lock); for (gp = gendisk_head; gp; gp = gp->next) if (!pos--) return gp; return NULL; } static void *part_next(struct seq_file *s, void *v, loff_t *pos) { ++*pos; return ((struct gendisk *)v)->next; } static void part_stop(struct seq_file *s, void *v) { read_unlock_irq(&gendisk_lock); } static int part_show(struct seq_file *s, void *v) { struct gendisk *gp = v; char buf[64]; int n; if (gp == gendisk_head) { seq_puts(s, "major minor #blocks name" #ifdef CONFIG_BLK_STATS " rio rmerge rsect ruse wio wmerge " "wsect wuse running use aveq" #endif "\n\n"); } /* show the full disk and all non-0 size partitions of it */ for (n = 0; n < (gp->nr_real << gp->minor_shift); n++) { if (gp->part[n].nr_sects) { #ifdef CONFIG_BLK_STATS struct hd_struct *hd = &gp->part[n]; disk_round_stats(hd); seq_printf(s, "%4d %4d %10d %s " "%u %u %u %u %u %u %u %u %d %u %u\n", gp->major, n, gp->sizes[n], disk_name(gp, n, buf), hd->rd_ios, hd->rd_merges, #define MSEC(x) ((x) * 1000 / HZ) hd->rd_sectors, MSEC(hd->rd_ticks), hd->wr_ios, hd->wr_merges, hd->wr_sectors, MSEC(hd->wr_ticks), hd->ios_in_flight, MSEC(hd->io_ticks), MSEC(hd->aveq)); #else seq_printf(s, "%4d %4d %10d %s\n", gp->major, n, gp->sizes[n], disk_name(gp, n, buf)); #endif /* CONFIG_BLK_STATS */ } } return 0; } struct seq_operations partitions_op = { .start = part_start, .next = part_next, .stop = part_stop, .show = part_show, }; #endif extern int blk_dev_init(void); extern int net_dev_init(void); extern void console_map_init(void); extern int atmdev_init(void); int __init device_init(void) { blk_dev_init(); sti(); #ifdef CONFIG_NET net_dev_init(); #endif #ifdef CONFIG_ATM (void) atmdev_init(); #endif #ifdef CONFIG_VT console_map_init(); #endif return 0; } __initcall(device_init);