blob: 6c52fa6051637b5565d8ef7a7d01f3ca8f4cd075 [file] [log] [blame]
/*
* 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>
/*
* 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);