/*
* Code extracted from drivers/block/genhd.c
* Copyright (C) 1991-1998 Linus Torvalds
* Re-organised Feb 1998 Russell King
*
* We now have independent partition support from the
* block drivers, which allows all the partition code to
* be grouped in one location, and it to be mostly self
* contained.
*
* Added needed MAJORS for new pairs, {hdi,hdj}, {hdk,hdl}
*/
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/blk.h>
#include <linux/init.h>
#include <linux/raid/md.h>
#include "check.h"
#include "acorn.h"
#include "amiga.h"
#include "atari.h"
#include "mac.h"
#include "msdos.h"
#include "osf.h"
#include "sgi.h"
#include "sun.h"
#include "ibm.h"
#include "ultrix.h"
extern void device_init(void);
extern int *blk_size[];
extern void rd_load(void);
extern void initrd_load(void);
struct gendisk *gendisk_head;
int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/
static int (*check_part[])(struct gendisk *hd, kdev_t dev, unsigned long first_sect, int first_minor) = {
#ifdef CONFIG_ACORN_PARTITION
acorn_partition,
#endif
#ifdef CONFIG_MSDOS_PARTITION
msdos_partition,
#endif
#ifdef CONFIG_OSF_PARTITION
osf_partition,
#endif
#ifdef CONFIG_SUN_PARTITION
sun_partition,
#endif
#ifdef CONFIG_AMIGA_PARTITION
amiga_partition,
#endif
#ifdef CONFIG_ATARI_PARTITION
atari_partition,
#endif
#ifdef CONFIG_MAC_PARTITION
mac_partition,
#endif
#ifdef CONFIG_SGI_PARTITION
sgi_partition,
#endif
#ifdef CONFIG_ULTRIX_PARTITION
ultrix_partition,
#endif
#ifdef CONFIG_IBM_PARTITION
ibm_partition,
#endif
NULL
};
/*
* disk_name() is used by genhd.c and blkpg.c.
* It formats the devicename of the indicated disk into
* the supplied buffer (of size at least 32), and returns
* a pointer to that same buffer (for convenience).
*/
84 char *disk_name (struct gendisk *hd, int minor, char *buf)
{
unsigned int part;
const char *maj = hd->major_name;
int unit = (minor >> hd->minor_shift) + 'a';
part = minor & ((1 << hd->minor_shift) - 1);
91 if (hd->part[minor].de) {
int pos;
pos = devfs_generate_path (hd->part[minor].de, buf, 64);
95 if (pos >= 0)
96 return buf + pos;
}
/*
* IDE devices use multiple major numbers, but the drives
* are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}..
* This requires special handling here.
*/
103 switch (hd->major) {
104 case IDE9_MAJOR:
unit += 2;
106 case IDE8_MAJOR:
unit += 2;
108 case IDE7_MAJOR:
unit += 2;
110 case IDE6_MAJOR:
unit += 2;
112 case IDE5_MAJOR:
unit += 2;
114 case IDE4_MAJOR:
unit += 2;
116 case IDE3_MAJOR:
unit += 2;
118 case IDE2_MAJOR:
unit += 2;
120 case IDE1_MAJOR:
unit += 2;
122 case IDE0_MAJOR:
maj = "hd";
124 break;
125 case MD_MAJOR:
sprintf(buf, "%s%d", maj, unit - 'a');
127 return buf;
}
129 if (hd->major >= SCSI_DISK1_MAJOR && hd->major <= SCSI_DISK7_MAJOR) {
unit = unit + (hd->major - SCSI_DISK1_MAJOR + 1) * 16;
131 if (unit > 'z') {
unit -= 'z' + 1;
sprintf(buf, "sd%c%c", 'a' + unit / 26, 'a' + unit % 26);
134 if (part)
sprintf(buf + 4, "%d", part);
136 return buf;
}
}
139 if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) {
int ctlr = hd->major - COMPAQ_SMART2_MAJOR;
int disk = minor >> hd->minor_shift;
int part = minor & (( 1 << hd->minor_shift) - 1);
143 if (part == 0)
sprintf(buf, "%s/c%dd%d", maj, ctlr, disk);
145 else
sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, disk, part);
147 return buf;
}
149 if (hd->major >= COMPAQ_CISS_MAJOR && hd->major <= COMPAQ_CISS_MAJOR+7) {
int ctlr = hd->major - COMPAQ_CISS_MAJOR;
int disk = minor >> hd->minor_shift;
int part = minor & (( 1 << hd->minor_shift) - 1);
153 if (part == 0)
sprintf(buf, "%s/c%dd%d", maj, ctlr, disk);
155 else
sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, disk, part);
157 return buf;
}
159 if (hd->major >= DAC960_MAJOR && hd->major <= DAC960_MAJOR+7) {
int ctlr = hd->major - DAC960_MAJOR;
int disk = minor >> hd->minor_shift;
int part = minor & (( 1 << hd->minor_shift) - 1);
163 if (part == 0)
sprintf(buf, "%s/c%dd%d", maj, ctlr, disk);
165 else
sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, disk, part);
167 return buf;
}
169 if (part)
sprintf(buf, "%s%c%d", maj, unit, part);
171 else
sprintf(buf, "%s%c", maj, unit);
173 return buf;
}
/*
* Add a partitions details to the devices partition description.
*/
179 void add_gd_partition(struct gendisk *hd, int minor, int start, int size)
{
#ifndef CONFIG_DEVFS_FS
char buf[40];
#endif
hd->part[minor].start_sect = start;
hd->part[minor].nr_sects = size;
#ifdef CONFIG_DEVFS_FS
printk(" p%d", (minor & ((1 << hd->minor_shift) - 1)));
#else
if ((hd->major >= COMPAQ_SMART2_MAJOR+0 && hd->major <= COMPAQ_SMART2_MAJOR+7) ||
191 (hd->major >= COMPAQ_CISS_MAJOR+0 && hd->major <= COMPAQ_CISS_MAJOR+7))
printk(" p%d", (minor & ((1 << hd->minor_shift) - 1)));
193 else
printk(" %s", disk_name(hd, minor, buf));
#endif
}
198 unsigned int get_ptable_blocksize(kdev_t dev)
{
int ret = 1024;
/*
* See whether the low-level driver has given us a minumum blocksize.
* If so, check to see whether it is larger than the default of 1024.
*/
206 if (!blksize_size[MAJOR(dev)])
207 return ret;
/*
* Check for certain special power of two sizes that we allow.
* With anything larger than 1024, we must force the blocksize up to
* the natural blocksize for the device so that we don't have to try
* and read partial sectors. Anything smaller should be just fine.
*/
216 switch (blksize_size[MAJOR(dev)][MINOR(dev)]) {
217 case 2048:
ret = 2048;
219 break;
220 case 4096:
ret = 4096;
222 break;
223 case 8192:
ret = 8192;
225 break;
226 case 1024:
227 case 512:
228 case 256:
229 case 0:
/*
* These are all OK.
*/
233 break;
234 default:
panic("Strange blocksize for partition table\n");
}
238 return ret;
}
#ifdef CONFIG_PROC_FS
242 int get_partition_list(char *page, char **start, off_t offset, int count)
{
struct gendisk *dsk;
int len;
len = sprintf(page, "major minor #blocks name\n\n");
248 for (dsk = gendisk_head; dsk; dsk = dsk->next) {
int n;
251 for (n = 0; n < (dsk->nr_real << dsk->minor_shift); n++)
252 if (dsk->part[n].nr_sects) {
char buf[64];
len += sprintf(page + len,
"%4d %4d %10d %s\n",
dsk->major, n, dsk->sizes[n],
disk_name(dsk, n, buf));
259 if (len < offset)
offset -= len, len = 0;
261 else if (len >= offset + count)
262 goto leave_loops;
}
}
leave_loops:
*start = page + offset;
len -= offset;
268 if (len < 0)
len = 0;
270 return len > count ? count : len;
}
#endif
274 static void check_partition(struct gendisk *hd, kdev_t dev, int first_part_minor)
{
devfs_handle_t de = NULL;
static int first_time = 1;
unsigned long first_sector;
char buf[64];
int i;
282 if (first_time)
printk(KERN_INFO "Partition check:\n");
first_time = 0;
first_sector = hd->part[MINOR(dev)].start_sect;
/*
* This is a kludge to allow the partition check to be
* skipped for specific drives (e.g. IDE CD-ROM drives)
*/
291 if ((int)first_sector == -1) {
hd->part[MINOR(dev)].start_sect = 0;
293 return;
}
296 if (hd->de_arr)
de = hd->de_arr[MINOR(dev) >> hd->minor_shift];
i = devfs_generate_path (de, buf, sizeof buf);
299 if (i >= 0)
printk(KERN_INFO " /dev/%s:", buf + i);
301 else
printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf));
303 for (i = 0; check_part[i]; i++)
304 if (check_part[i](hd, dev, first_sector, first_part_minor))
305 goto setup_devfs;
printk(" unknown partition table\n");
setup_devfs:
i = first_part_minor - 1;
devfs_register_partitions (hd, i, hd->sizes ? 0 : 1);
}
#ifdef CONFIG_DEVFS_FS
static void devfs_register_partition (struct gendisk *dev, int minor, int part)
{
int devnum = minor >> dev->minor_shift;
devfs_handle_t dir;
unsigned int devfs_flags = DEVFS_FL_DEFAULT;
char devname[16];
if (dev->part[minor + part].de) return;
dir = devfs_get_parent (dev->part[minor].de);
if (!dir) return;
if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) )
devfs_flags |= DEVFS_FL_REMOVABLE;
sprintf (devname, "part%d", part);
dev->part[minor + part].de =
devfs_register (dir, devname, devfs_flags,
dev->major, minor + part,
S_IFBLK | S_IRUSR | S_IWUSR,
dev->fops, NULL);
}
static void devfs_register_disc (struct gendisk *dev, int minor)
{
int pos = 0;
int devnum = minor >> dev->minor_shift;
devfs_handle_t dir, slave;
unsigned int devfs_flags = DEVFS_FL_DEFAULT;
char dirname[64], symlink[16];
static unsigned int disc_counter;
static devfs_handle_t devfs_handle;
if (dev->part[minor].de) return;
if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) )
devfs_flags |= DEVFS_FL_REMOVABLE;
if (dev->de_arr) {
dir = dev->de_arr[devnum];
if (!dir) /* Aware driver wants to block disc management */
return;
pos = devfs_generate_path (dir, dirname + 3, sizeof dirname-3);
if (pos < 0) return;
strncpy (dirname + pos, "../", 3);
}
else {
/* Unaware driver: construct "real" directory */
sprintf (dirname, "../%s/disc%d", dev->major_name, devnum);
dir = devfs_mk_dir (NULL, dirname + 3, NULL);
}
if (!devfs_handle)
devfs_handle = devfs_mk_dir (NULL, "discs", NULL);
sprintf (symlink, "disc%u", disc_counter++);
devfs_mk_symlink (devfs_handle, symlink, DEVFS_FL_DEFAULT,
dirname + pos, &slave, NULL);
dev->part[minor].de =
devfs_register (dir, "disc", devfs_flags, dev->major, minor,
S_IFBLK | S_IRUSR | S_IWUSR, dev->fops, NULL);
devfs_auto_unregister (dev->part[minor].de, slave);
if (!dev->de_arr)
devfs_auto_unregister (slave, dir);
}
#endif /* CONFIG_DEVFS_FS */
374 void devfs_register_partitions (struct gendisk *dev, int minor, int unregister)
{
#ifdef CONFIG_DEVFS_FS
int part;
if (!unregister)
devfs_register_disc (dev, minor);
for (part = 1; part < dev->max_p; part++) {
if ( unregister || (dev->part[part + minor].nr_sects < 1) ) {
devfs_unregister (dev->part[part + minor].de);
dev->part[part + minor].de = NULL;
continue;
}
devfs_register_partition (dev, minor, part);
}
if (unregister) {
devfs_unregister (dev->part[minor].de);
dev->part[minor].de = NULL;
}
#endif /* CONFIG_DEVFS_FS */
}
/*
* This function will re-read the partition tables for a given device,
* and set things back up again. There are some important caveats,
* however. You must ensure that no one is using the device, and no one
* can start using the device while this function is being executed.
*
* Much of the cleanup from the old partition tables should have already been
* done
*/
406 void register_disk(struct gendisk *gdev, kdev_t dev, unsigned minors,
struct block_device_operations *ops, long size)
{
409 if (!gdev)
410 return;
grok_partitions(gdev, MINOR(dev)>>gdev->minor_shift, minors, size);
}
414 void grok_partitions(struct gendisk *dev, int drive, unsigned minors, long size)
{
int i;
int first_minor = drive << dev->minor_shift;
int end_minor = first_minor + dev->max_p;
420 if(!dev->sizes)
blk_size[dev->major] = NULL;
dev->part[first_minor].nr_sects = size;
/* No Such Agen^Wdevice or no minors to use for partitions */
425 if (!size || minors == 1)
426 return;
blk_size[dev->major] = NULL;
check_partition(dev, MKDEV(dev->major, first_minor), 1 + first_minor);
/*
* We need to set the sizes array before we will be able to access
* any of the partitions on this device.
*/
435 if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
436 for (i = first_minor; i < end_minor; i++)
dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
blk_size[dev->major] = dev->sizes;
}
}
442 int __init partition_setup(void)
{
device_init();
#ifdef CONFIG_BLK_DEV_RAM
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && mount_initrd) initrd_load();
else
#endif
rd_load();
#endif
453 return 0;
}
__initcall(partition_setup);