📄 medley.c
字号:
/* * MEDLEY SOFTWARE RAID DRIVER (Silicon Image 3112 and others) * * Copyright (C) 2003 Thomas Horsten <thomas@horsten.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Copyright (C) 2003 Thomas Horsten <thomas@horsten.com> * All Rights Reserved. * * This driver uses the ATA RAID driver framework and is based on * code from Arjan van de Ven's silraid.c and hptraid.c. * * It is a driver for the Medley software RAID, which is used by * some IDE controllers, including the Silicon Image 3112 SATA * controller found onboard many modern motherboards, and the * CMD680 stand-alone PCI RAID controller. * * The author has only tested this on the Silicon Image SiI3112. * If you have any luck using more than 2 drives, and/or more * than one RAID set, and/or any other chip than the SiI3112, * please let me know by sending me mail at the above address. * * Currently, only striped mode is supported for these RAIDs. * * You are welcome to contact me if you have any questions or * suggestions for improvement. * * Change history: * * 20040310 - thomas@horsten.com * Removed C99 style variable declarations that confused gcc-2.x * Fixed a bug where more than one RAID set would not be detected correctly * General cleanup for submission to kernel * * 20031012 - thomas@horsten.com * Added support for BLKRRPART ioctl to re-read partition table * * 20030801 - thomas@horsten.com * First test release * */#include <linux/version.h>#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/smp_lock.h>#include <linux/blkdev.h>#include <linux/blkpg.h>#include <linux/genhd.h>#include <linux/ioctl.h>#include <linux/ide.h>#include <asm/uaccess.h>#include "ataraid.h"/* * These options can be tuned if the need should occur. * * Even better, this driver could be changed to allocate the structures * dynamically. */#define MAX_DRIVES_PER_SET 8#define MAX_MEDLEY_ARRAYS 4/* * Set to 1 only if you are debugging the driver, or if it doesn't work * the way you expect and you want to to report it. * * This will produce lots of kernel messages, some of which might * help me figure out what is going wrong). */#define DEBUGGING_MEDLEY 0#if DEBUGGING_MEDLEY#define dprintk(fmt, args...) printk(fmt, ##args)#else#define dprintk(fmt, args...)#endif/* * Medley RAID metadata structure. * * The metadata structure is based on the ATA drive ID from the drive itself, * with the RAID information in the vendor specific regions. * * We do not use all the fields, since we only support Striped Sets. */struct medley_metadata { u8 driveid0[46]; u8 ascii_version[8]; u8 driveid1[52]; u32 total_sectors_low; u32 total_sectors_high; u16 reserved0; u8 driveid2[142]; u16 product_id; u16 vendor_id; u16 minor_ver; u16 major_ver; u16 creation_timestamp[3]; u16 chunk_size; u16 reserved1; u8 drive_number; u8 raid_type; u8 drives_per_striped_set; u8 striped_set_number; u8 drives_per_mirrored_set; u8 mirrored_set_number; u32 rebuild_ptr_low; u32 rebuild_ptr_high; u32 incarnation_no; u8 member_status; u8 mirrored_set_state; u8 reported_device_location; u8 member_location; u8 auto_rebuild; u8 reserved3[17]; u16 checksum;};/* * This struct holds the information about a Medley array */struct medley_array { u8 drives; u16 chunk_size; u32 sectors_per_row; u8 chunk_size_log; u16 present; u16 timestamp[3]; u32 sectors; int registered; atomic_t valid; int access; kdev_t members[MAX_DRIVES_PER_SET]; struct block_device *bdev[MAX_DRIVES_PER_SET];};static struct medley_array raid[MAX_MEDLEY_ARRAYS];/* * Here we keep the offset of the ATARAID device ID's compared to our * own (this will normally be 0, unless another ATARAID driver has * registered some arrays before us). */static int medley_devid_offset = 0;/* * This holds the number of detected arrays. */static int medley_arrays = 0;/* * Wait queue for opening device (used when re-reading partition table) */static DECLARE_WAIT_QUEUE_HEAD(medley_wait_open);/* * The interface functions used by the ataraid framework. */static int medley_open(struct inode *inode, struct file *filp);static int medley_release(struct inode *inode, struct file *filp);static int medley_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static int medley_make_request(request_queue_t * q, int rw, struct buffer_head *bh);static struct raid_device_operations medley_ops = { open: medley_open, release: medley_release, ioctl: medley_ioctl, make_request: medley_make_request};/* * This is the list of devices to probe. */static const kdev_t probelist[] = { MKDEV(IDE0_MAJOR, 0), MKDEV(IDE0_MAJOR, 64), MKDEV(IDE1_MAJOR, 0), MKDEV(IDE1_MAJOR, 64), MKDEV(IDE2_MAJOR, 0), MKDEV(IDE2_MAJOR, 64), MKDEV(IDE3_MAJOR, 0), MKDEV(IDE3_MAJOR, 64), MKDEV(IDE4_MAJOR, 0), MKDEV(IDE4_MAJOR, 64), MKDEV(IDE5_MAJOR, 0), MKDEV(IDE5_MAJOR, 64), MKDEV(IDE6_MAJOR, 0), MKDEV(IDE6_MAJOR, 64), MKDEV(0, 0)};/* * Handler for ioctl calls to the virtual device */static int medley_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ unsigned int minor; unsigned long sectors; int devminor = (inode->i_rdev >> SHIFT) & MAJOR_MASK; int device = devminor - medley_devid_offset; int partition; dprintk("medley_ioctl\n"); minor = MINOR(inode->i_rdev) >> SHIFT; switch (cmd) { case BLKGETSIZE: /* Return device size */ if (!arg) return -EINVAL; sectors = ataraid_gendisk.part[MINOR(inode->i_rdev)].nr_sects; dprintk("medley_ioctl: BLKGETSIZE\n"); if (MINOR(inode->i_rdev) & 15) return put_user(sectors, (unsigned long *) arg); return put_user(raid[minor - medley_devid_offset].sectors, (unsigned long *) arg); break; case HDIO_GETGEO: { struct hd_geometry *loc = (struct hd_geometry *) arg; unsigned short bios_cyl = (unsigned short) (raid[minor].sectors / 255 / 63); /* truncate */ dprintk("medley_ioctl: HDIO_GETGEO\n"); if (!loc) return -EINVAL; if (put_user(255, (byte *) & loc->heads)) return -EFAULT; if (put_user(63, (byte *) & loc->sectors)) return -EFAULT; if (put_user (bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT; if (put_user ((unsigned) ataraid_gendisk. part[MINOR(inode->i_rdev)].start_sect, (unsigned long *) &loc->start)) return -EFAULT; return 0; } case HDIO_GETGEO_BIG: { struct hd_big_geometry *loc = (struct hd_big_geometry *) arg; dprintk("medley_ioctl: HDIO_GETGEO_BIG\n"); if (!loc) return -EINVAL; if (put_user(255, (byte *) & loc->heads)) return -EFAULT; if (put_user(63, (byte *) & loc->sectors)) return -EFAULT; if (put_user (raid[minor - medley_devid_offset].sectors / 255 / 63, (unsigned int *) &loc->cylinders)) return -EFAULT; if (put_user ((unsigned) ataraid_gendisk. part[MINOR(inode->i_rdev)].start_sect, (unsigned long *) &loc->start)) return -EFAULT; return 0; } case BLKROSET: case BLKROGET: case BLKSSZGET: dprintk("medley_ioctl: BLK*\n"); return blk_ioctl(inode->i_rdev, cmd, arg); case BLKRRPART: /* Re-read partition tables */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (minor != 0) return -EINVAL; if (atomic_read(&(raid[device].valid)) == 0) return -EINVAL; atomic_set(&(raid[device].valid), 0); if (raid[device].access != 1) { atomic_set(&(raid[device].valid), 1); return -EBUSY; } for (partition = 15; partition >= 0; partition--) { invalidate_device(MKDEV(ATARAID_MAJOR, partition + devminor), 1); ataraid_gendisk.part[partition + devminor].start_sect = 0; ataraid_gendisk.part[partition + devminor].nr_sects = 0; } ataraid_register_disk(device, raid[device].sectors); atomic_set(&(raid[device].valid), 1); wake_up(&medley_wait_open); return 0; default: return -EINVAL; } return 0;}/* * Handler to map a request to the real device. * If the request cannot be made because it spans multiple disks, * we return -1, otherwise we modify the request and return 1. */static int medley_make_request(request_queue_t * q, int rw, struct buffer_head *bh){ u8 disk; u32 rsect = bh->b_rsector; int device = ((bh->b_rdev >> SHIFT) & MAJOR_MASK) - medley_devid_offset; struct medley_array *r = raid + device; /* Add the partition offset */ rsect = rsect + ataraid_gendisk.part[MINOR(bh->b_rdev)].start_sect; dprintk("medley_make_request, rsect=%ul\n", rsect); /* Detect if the request crosses a chunk barrier */ if (r->chunk_size_log) { if (((rsect & (r->chunk_size - 1)) + (bh->b_size / 512)) > (1 << r->chunk_size_log)) { return -1; } } else { if ((rsect / r->chunk_size) != ((rsect + (bh->b_size / 512) - 1) / r->chunk_size)) { return -1; } } /* * Medley arrays are simple enough, since the smallest disk decides the * number of sectors used per disk. So there is no need for the cutoff * magic found in other drivers like hptraid. */ if (r->chunk_size_log) { /* We save some expensive operations (1 div, 1 mul, 1 mod), * if the chunk size is a power of 2, which is true in most * cases (at least with my version of the RAID BIOS). */ disk = (rsect >> r->chunk_size_log) % r->drives; rsect = ((rsect / r->sectors_per_row) << r->chunk_size_log) + (rsect & (r->chunk_size - 1)); } else { disk = (rsect / r->chunk_size) % r->drives; rsect = rsect / r->sectors_per_row * r->chunk_size +
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -