/* raid0.c : Multiple Devices driver for Linux Copyright (C) 1994-96 Marc ZYNGIER or RAID-0 management functions. 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, or (at your option) any later version. You should have received a copy of the GNU General Public License (for example /usr/src/linux/COPYING); if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #define MAJOR_NR MD_MAJOR #define MD_DRIVER #define MD_PERSONALITY static int create_strip_zones (int minor, struct md_dev *mddev) { int i, j, c=0; int current_offset=0; struct real_dev *smallest_by_zone; struct raid0_data *data=(struct raid0_data *) mddev->private; data->nr_strip_zones=1; for (i=1; inb_dev; i++) { for (j=0; jdevices[i].size==mddev->devices[j].size) { c=1; break; } if (!c) data->nr_strip_zones++; c=0; } if ((data->strip_zone=vmalloc(sizeof(struct strip_zone)*data->nr_strip_zones)) == NULL) return 1; data->smallest=NULL; for (i=0; inr_strip_zones; i++) { data->strip_zone[i].dev_offset=current_offset; smallest_by_zone=NULL; c=0; for (j=0; jnb_dev; j++) if (mddev->devices[j].size>current_offset) { data->strip_zone[i].dev[c++]=mddev->devices+j; if (!smallest_by_zone || smallest_by_zone->size > mddev->devices[j].size) smallest_by_zone=mddev->devices+j; } data->strip_zone[i].nb_dev=c; data->strip_zone[i].size=(smallest_by_zone->size-current_offset)*c; if (!data->smallest || data->smallest->size > data->strip_zone[i].size) data->smallest=data->strip_zone+i; data->strip_zone[i].zone_offset=i ? (data->strip_zone[i-1].zone_offset+ data->strip_zone[i-1].size) : 0; current_offset=smallest_by_zone->size; } return 0; } static int raid0_run (int minor, struct md_dev *mddev) { int cur=0, i=0, size, zone0_size, nb_zone; struct raid0_data *data; MOD_INC_USE_COUNT; if ((mddev->private=vmalloc (sizeof (struct raid0_data))) == NULL) return 1; data=(struct raid0_data *) mddev->private; if (create_strip_zones (minor, mddev)) { vfree(data); return 1; } nb_zone=data->nr_zones= md_size[minor]/data->smallest->size + (md_size[minor]%data->smallest->size ? 1 : 0); printk ("raid0 : Allocating %d bytes for hash.\n",sizeof(struct raid0_hash)*nb_zone); if ((data->hash_table=vmalloc (sizeof (struct raid0_hash)*nb_zone)) == NULL) { vfree(data->strip_zone); vfree(data); return 1; } size=data->strip_zone[cur].size; i=0; while (curnr_strip_zones) { data->hash_table[i].zone0=data->strip_zone+cur; if (size>=data->smallest->size)/* If we completely fill the slot */ { data->hash_table[i++].zone1=NULL; size-=data->smallest->size; if (!size) { if (++cur==data->nr_strip_zones) continue; size=data->strip_zone[cur].size; } continue; } if (++cur==data->nr_strip_zones) /* Last dev, set unit1 as NULL */ { data->hash_table[i].zone1=NULL; continue; } zone0_size=size; /* Here, we use a 2nd dev to fill the slot */ size=data->strip_zone[cur].size; data->hash_table[i++].zone1=data->strip_zone+cur; size-=(data->smallest->size - zone0_size); } return (0); } static int raid0_stop (int minor, struct md_dev *mddev) { struct raid0_data *data=(struct raid0_data *) mddev->private; vfree (data->hash_table); vfree (data->strip_zone); vfree (data); MOD_DEC_USE_COUNT; return 0; } /* * FIXME - We assume some things here : * - requested buffers NEVER bigger than chunk size, * - requested buffers NEVER cross stripes limits. * Of course, those facts may not be valid anymore (and surely won't...) * Hey guys, there's some work out there ;-) */ static int raid0_map (struct md_dev *mddev, kdev_t *rdev, unsigned long *rsector, unsigned long size) { struct raid0_data *data=(struct raid0_data *) mddev->private; static struct raid0_hash *hash; struct strip_zone *zone; struct real_dev *tmp_dev; int blk_in_chunk, factor, chunk, chunk_size; long block, rblock; factor=FACTOR(mddev); chunk_size=(1UL << FACTOR_SHIFT(factor)); block=*rsector >> 1; hash=data->hash_table+(block/data->smallest->size); /* Sanity check */ if ((chunk_size*2)<(*rsector % (chunk_size*2))+size) { printk ("raid0_convert : can't convert block across chunks or bigger than %dk %ld %ld\n", chunk_size, *rsector, size); return (-1); } if (block >= (hash->zone0->size + hash->zone0->zone_offset)) { if (!hash->zone1) { printk ("raid0_convert : hash->zone1==NULL for block %ld\n", block); return (-1); } zone=hash->zone1; } else zone=hash->zone0; blk_in_chunk=block & (chunk_size -1); chunk=(block - zone->zone_offset) / (zone->nb_dev<dev[(block >> FACTOR_SHIFT(factor)) % zone->nb_dev]; rblock=(chunk << FACTOR_SHIFT(factor)) + blk_in_chunk + zone->dev_offset; *rdev=tmp_dev->dev; *rsector=rblock<<1; return (0); } static int raid0_status (char *page, int minor, struct md_dev *mddev) { int sz=0; #undef MD_DEBUG #ifdef MD_DEBUG int j, k; struct raid0_data *data=(struct raid0_data *) mddev->private; sz+=sprintf (page+sz, " "); for (j=0; jnr_zones; j++) { sz+=sprintf (page+sz, "[z%d", data->hash_table[j].zone0-data->strip_zone); if (data->hash_table[j].zone1) sz+=sprintf (page+sz, "/z%d] ", data->hash_table[j].zone1-data->strip_zone); else sz+=sprintf (page+sz, "] "); } sz+=sprintf (page+sz, "\n"); for (j=0; jnr_strip_zones; j++) { sz+=sprintf (page+sz, " z%d=[", j); for (k=0; kstrip_zone[j].nb_dev; k++) sz+=sprintf (page+sz, "%s/", partition_name(data->strip_zone[j].dev[k]->dev)); sz--; sz+=sprintf (page+sz, "] zo=%d do=%d s=%d\n", data->strip_zone[j].zone_offset, data->strip_zone[j].dev_offset, data->strip_zone[j].size); } #endif sz+=sprintf (page+sz, " %dk chunks", 1<