1/* 2 raid0.c : Multiple Devices driver for Linux 3 Copyright (C) 1994-96 Marc ZYNGIER 4 <zyngier@ufr-info-p7.ibp.fr> or 5 <maz@gloups.fdn.fr> 6 Copyright (C) 1999, 2000 Ingo Molnar, Red Hat 7 8 9 RAID-0 management functions. 10 11 This program is free software; you can redistribute it and/or modify 12 it under the terms of the GNU General Public License as published by 13 the Free Software Foundation; either version 2, or (at your option) 14 any later version. 15 16 You should have received a copy of the GNU General Public License 17 (for example /usr/src/linux/COPYING); if not, write to the Free 18 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19*/ 20 21#include <linux/module.h> 22#include <linux/raid/raid0.h> 23 24#define MAJOR_NR MD_MAJOR 25#define MD_DRIVER 26#define MD_PERSONALITY 27 28static int create_strip_zones (mddev_t *mddev) 29{ 30 int i, c, j, j1, j2; 31 unsigned long current_offset, curr_zone_offset; 32 raid0_conf_t *conf = mddev_to_conf(mddev); 33 mdk_rdev_t *smallest, *rdev1, *rdev2, *rdev; 34 35 /* 36 * The number of 'same size groups' 37 */ 38 conf->nr_strip_zones = 0; 39 40 ITERATE_RDEV_ORDERED(mddev,rdev1,j1) { 41 printk("raid0: looking at %s\n", partition_name(rdev1->dev)); 42 c = 0; 43 ITERATE_RDEV_ORDERED(mddev,rdev2,j2) { 44 printk("raid0: comparing %s(%ld) with %s(%ld)\n", partition_name(rdev1->dev), rdev1->size, partition_name(rdev2->dev), rdev2->size); 45 if (rdev2 == rdev1) { 46 printk("raid0: END\n"); 47 break; 48 } 49 if (rdev2->size == rdev1->size) 50 { 51 /* 52 * Not unique, dont count it as a new 53 * group 54 */ 55 printk("raid0: EQUAL\n"); 56 c = 1; 57 break; 58 } 59 printk("raid0: NOT EQUAL\n"); 60 } 61 if (!c) { 62 printk("raid0: ==> UNIQUE\n"); 63 conf->nr_strip_zones++; 64 printk("raid0: %d zones\n", conf->nr_strip_zones); 65 } 66 } 67 printk("raid0: FINAL %d zones\n", conf->nr_strip_zones); 68 69 conf->strip_zone = vmalloc(sizeof(struct strip_zone)* 70 conf->nr_strip_zones); 71 if (!conf->strip_zone) 72 return 1; 73 74 75 conf->smallest = NULL; 76 current_offset = 0; 77 curr_zone_offset = 0; 78 79 for (i = 0; i < conf->nr_strip_zones; i++) 80 { 81 struct strip_zone *zone = conf->strip_zone + i; 82 83 printk("raid0: zone %d\n", i); 84 zone->dev_offset = current_offset; 85 smallest = NULL; 86 c = 0; 87 88 ITERATE_RDEV_ORDERED(mddev,rdev,j) { 89 90 printk("raid0: checking %s ...", partition_name(rdev->dev)); 91 if (rdev->size > current_offset) 92 { 93 printk(" contained as device %d\n", c); 94 zone->dev[c] = rdev; 95 c++; 96 if (!smallest || (rdev->size <smallest->size)) { 97 smallest = rdev; 98 printk(" (%ld) is smallest!.\n", rdev->size); 99 } 100 } else 101 printk(" nope.\n"); 102 } 103 104 zone->nb_dev = c; 105 zone->size = (smallest->size - current_offset) * c; 106 printk("raid0: zone->nb_dev: %d, size: %ld\n",zone->nb_dev,zone->size); 107 108 if (!conf->smallest || (zone->size < conf->smallest->size)) 109 conf->smallest = zone; 110 111 zone->zone_offset = curr_zone_offset; 112 curr_zone_offset += zone->size; 113 114 current_offset = smallest->size; 115 printk("raid0: current zone offset: %ld\n", current_offset); 116 } 117 printk("raid0: done.\n"); 118 return 0; 119} 120 121static int raid0_run (mddev_t *mddev) 122{ 123 unsigned long cur=0, i=0, size, zone0_size, nb_zone; 124 raid0_conf_t *conf; 125 126 MOD_INC_USE_COUNT; 127 128 conf = vmalloc(sizeof (raid0_conf_t)); 129 if (!conf) 130 goto out; 131 mddev->private = (void *)conf; 132 133 if (md_check_ordering(mddev)) { 134 printk("raid0: disks are not ordered, aborting!\n"); 135 goto out_free_conf; 136 } 137 138 if (create_strip_zones (mddev)) 139 goto out_free_conf; 140 141 printk("raid0 : md_size is %d blocks.\n", md_size[mdidx(mddev)]); 142 printk("raid0 : conf->smallest->size is %ld blocks.\n", conf->smallest->size); 143 nb_zone = md_size[mdidx(mddev)]/conf->smallest->size + 144 (md_size[mdidx(mddev)] % conf->smallest->size ? 1 : 0); 145 printk("raid0 : nb_zone is %ld.\n", nb_zone); 146 conf->nr_zones = nb_zone; 147 148 printk("raid0 : Allocating %ld bytes for hash.\n", 149 nb_zone*sizeof(struct raid0_hash)); 150 151 conf->hash_table = vmalloc (sizeof (struct raid0_hash)*nb_zone); 152 if (!conf->hash_table) 153 goto out_free_zone_conf; 154 size = conf->strip_zone[cur].size; 155 156 i = 0; 157 while (cur < conf->nr_strip_zones) { 158 conf->hash_table[i].zone0 = conf->strip_zone + cur; 159 160 /* 161 * If we completely fill the slot 162 */ 163 if (size >= conf->smallest->size) { 164 conf->hash_table[i++].zone1 = NULL; 165 size -= conf->smallest->size; 166 167 if (!size) { 168 if (++cur == conf->nr_strip_zones) 169 continue; 170 size = conf->strip_zone[cur].size; 171 } 172 continue; 173 } 174 if (++cur == conf->nr_strip_zones) { 175 /* 176 * Last dev, set unit1 as NULL 177 */ 178 conf->hash_table[i].zone1=NULL; 179 continue; 180 } 181 182 /* 183 * Here we use a 2nd dev to fill the slot 184 */ 185 zone0_size = size; 186 size = conf->strip_zone[cur].size; 187 conf->hash_table[i++].zone1 = conf->strip_zone + cur; 188 size -= (conf->smallest->size - zone0_size); 189 } 190 return 0; 191 192out_free_zone_conf: 193 vfree(conf->strip_zone); 194 conf->strip_zone = NULL; 195 196out_free_conf: 197 vfree(conf); 198 mddev->private = NULL; 199out: 200 MOD_DEC_USE_COUNT; 201 return 1; 202} 203 204static int raid0_stop (mddev_t *mddev) 205{ 206 raid0_conf_t *conf = mddev_to_conf(mddev); 207 208 vfree (conf->hash_table); 209 conf->hash_table = NULL; 210 vfree (conf->strip_zone); 211 conf->strip_zone = NULL; 212 vfree (conf); 213 mddev->private = NULL; 214 215 MOD_DEC_USE_COUNT; 216 return 0; 217} 218 219static int raid0_make_request (mddev_t *mddev, 220 int rw, struct buffer_head * bh) 221{ 222 unsigned int sect_in_chunk, chunksize_bits, chunk_size; 223 raid0_conf_t *conf = mddev_to_conf(mddev); 224 struct raid0_hash *hash; 225 struct strip_zone *zone; 226 mdk_rdev_t *tmp_dev; 227 unsigned long chunk, block, rsect; 228 229 chunk_size = mddev->param.chunk_size >> 10; 230 chunksize_bits = ffz(~chunk_size); 231 block = bh->b_rsector >> 1; 232 hash = conf->hash_table + block / conf->smallest->size; 233 234 /* Sanity check */ 235 if (chunk_size < (block % chunk_size) + (bh->b_size >> 10)) 236 goto bad_map; 237 238 if (!hash) 239 goto bad_hash; 240 241 if (!hash->zone0) 242 goto bad_zone0; 243 244 if (block >= (hash->zone0->size + hash->zone0->zone_offset)) { 245 if (!hash->zone1) 246 goto bad_zone1; 247 zone = hash->zone1; 248 } else 249 zone = hash->zone0; 250 251 sect_in_chunk = bh->b_rsector & ((chunk_size<<1) -1); 252 chunk = (block - zone->zone_offset) / (zone->nb_dev << chunksize_bits); 253 tmp_dev = zone->dev[(block >> chunksize_bits) % zone->nb_dev]; 254 rsect = (((chunk << chunksize_bits) + zone->dev_offset)<<1) 255 + sect_in_chunk; 256 257 /* 258 * The new BH_Lock semantics in ll_rw_blk.c guarantee that this 259 * is the only IO operation happening on this bh. 260 */ 261 bh->b_rdev = tmp_dev->dev; 262 bh->b_rsector = rsect; 263 264 /* 265 * Let the main block layer submit the IO and resolve recursion: 266 */ 267 return 1; 268 269bad_map: 270 printk ("raid0_make_request bug: can't convert block across chunks or bigger than %dk %ld %d\n", chunk_size, bh->b_rsector, bh->b_size >> 10); 271 goto outerr; 272bad_hash: 273 printk("raid0_make_request bug: hash==NULL for block %ld\n", block); 274 goto outerr; 275bad_zone0: 276 printk ("raid0_make_request bug: hash->zone0==NULL for block %ld\n", block); 277 goto outerr; 278bad_zone1: 279 printk ("raid0_make_request bug: hash->zone1==NULL for block %ld\n", block); 280 outerr: 281 buffer_IO_error(bh); 282 return 0; 283} 284 285static int raid0_status (char *page, mddev_t *mddev) 286{ 287 int sz = 0; 288#undef MD_DEBUG 289#ifdef MD_DEBUG 290 int j, k; 291 raid0_conf_t *conf = mddev_to_conf(mddev); 292 293 sz += sprintf(page + sz, " "); 294 for (j = 0; j < conf->nr_zones; j++) { 295 sz += sprintf(page + sz, "[z%d", 296 conf->hash_table[j].zone0 - conf->strip_zone); 297 if (conf->hash_table[j].zone1) 298 sz += sprintf(page+sz, "/z%d] ", 299 conf->hash_table[j].zone1 - conf->strip_zone); 300 else 301 sz += sprintf(page+sz, "] "); 302 } 303 304 sz += sprintf(page + sz, "\n"); 305 306 for (j = 0; j < conf->nr_strip_zones; j++) { 307 sz += sprintf(page + sz, " z%d=[", j); 308 for (k = 0; k < conf->strip_zone[j].nb_dev; k++) 309 sz += sprintf (page+sz, "%s/", partition_name( 310 conf->strip_zone[j].dev[k]->dev)); 311 sz--; 312 sz += sprintf (page+sz, "] zo=%d do=%d s=%d\n", 313 conf->strip_zone[j].zone_offset, 314 conf->strip_zone[j].dev_offset, 315 conf->strip_zone[j].size); 316 } 317#endif 318 sz += sprintf(page + sz, " %dk chunks", mddev->param.chunk_size/1024); 319 return sz; 320} 321 322static mdk_personality_t raid0_personality= 323{ 324 name: "raid0", 325 make_request: raid0_make_request, 326 run: raid0_run, 327 stop: raid0_stop, 328 status: raid0_status, 329}; 330 331static int md__init raid0_init (void) 332{ 333 return register_md_personality (RAID0, &raid0_personality); 334} 335 336static void raid0_exit (void) 337{ 338 unregister_md_personality (RAID0); 339} 340 341module_init(raid0_init); 342module_exit(raid0_exit); 343MODULE_LICENSE("GPL"); 344