1 /*
2 silraid.c Copyright (C) 2002 Red Hat, Inc. All rights reserved.
3
4 The contents of this file are subject to the Open Software License version 1.1
5 that can be found at http://www.opensource.org/licenses/osl-1.1.txt and is
6 included herein by reference.
7
8 Alternatively, the contents of this file may be used under the
9 terms of the GNU General Public License version 2 (the "GPL") as
10 distributed in the kernel source COPYING file, in which
11 case the provisions of the GPL are applicable instead of the
12 above. If you wish to allow the use of your version of this file
13 only under the terms of the GPL and not to allow others to use
14 your version of this file under the OSL, indicate your decision
15 by deleting the provisions above and replace them with the notice
16 and other provisions required by the GPL. If you do not delete
17 the provisions above, a recipient may use your version of this
18 file under either the OSL or the GPL.
19
20 Authors: Arjan van de Ven <arjanv@redhat.com>
21
22
23 */
24
25 #include <linux/module.h>
26 #include <linux/init.h>
27 #include <linux/kernel.h>
28 #include <linux/sched.h>
29 #include <linux/smp_lock.h>
30 #include <linux/blkdev.h>
31 #include <linux/blkpg.h>
32 #include <linux/genhd.h>
33 #include <linux/ioctl.h>
34
35 #include <linux/ide.h>
36 #include <asm/uaccess.h>
37
38 #include "ataraid.h"
39
40 static int silraid_open(struct inode * inode, struct file * filp);
41 static int silraid_release(struct inode * inode, struct file * filp);
42 static int silraid_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
43 static int silraid0_make_request (request_queue_t *q, int rw, struct buffer_head * bh);
44
45 struct disk_dev {
46 int major;
47 int minor;
48 int device;
49 };
50
51 static struct disk_dev devlist[]= {
52 {IDE0_MAJOR, 0, -1 },
53 {IDE0_MAJOR, 64, -1 },
54 {IDE1_MAJOR, 0, -1 },
55 {IDE1_MAJOR, 64, -1 },
56 {IDE2_MAJOR, 0, -1 },
57 {IDE2_MAJOR, 64, -1 },
58 {IDE3_MAJOR, 0, -1 },
59 {IDE3_MAJOR, 64, -1 },
60 {IDE4_MAJOR, 0, -1 },
61 {IDE4_MAJOR, 64, -1 },
62 {IDE5_MAJOR, 0, -1 },
63 {IDE5_MAJOR, 64, -1 },
64 {IDE6_MAJOR, 0, -1 },
65 {IDE6_MAJOR, 64, -1 },
66 };
67
68
69 struct sildisk {
70 kdev_t device;
71 unsigned long sectors;
72 struct block_device *bdev;
73 unsigned long last_pos;
74 };
75
76 struct silraid {
77 unsigned int stride;
78 unsigned int disks;
79 unsigned long sectors;
80 struct geom geom;
81
82 struct sildisk disk[8];
83
84 unsigned long cutoff[8];
85 unsigned int cutoff_disks[8];
86 };
87
88 static struct raid_device_operations silraid0_ops = {
89 open: silraid_open,
90 release: silraid_release,
91 ioctl: silraid_ioctl,
92 make_request: silraid0_make_request
93 };
94
95 static struct silraid raid[16];
96
97
silraid_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg)98 static int silraid_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
99 {
100 unsigned int minor;
101 unsigned long sectors;
102
103 if (!inode || !inode->i_rdev)
104 return -EINVAL;
105
106 minor = MINOR(inode->i_rdev)>>SHIFT;
107
108 switch (cmd) {
109
110 case BLKGETSIZE: /* Return device size */
111 if (!arg) return -EINVAL;
112 sectors = ataraid_gendisk.part[MINOR(inode->i_rdev)].nr_sects;
113 if (MINOR(inode->i_rdev)&15)
114 return put_user(sectors, (unsigned long *) arg);
115 return put_user(raid[minor].sectors , (unsigned long *) arg);
116 break;
117
118
119 case HDIO_GETGEO:
120 {
121 struct hd_geometry *loc = (struct hd_geometry *) arg;
122 unsigned short bios_cyl = raid[minor].geom.cylinders; /* truncate */
123
124 if (!loc) return -EINVAL;
125 if (put_user(raid[minor].geom.heads, (byte *) &loc->heads)) return -EFAULT;
126 if (put_user(raid[minor].geom.sectors, (byte *) &loc->sectors)) return -EFAULT;
127 if (put_user(bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT;
128 if (put_user((unsigned)ataraid_gendisk.part[MINOR(inode->i_rdev)].start_sect,
129 (unsigned long *) &loc->start)) return -EFAULT;
130 return 0;
131 }
132
133 case HDIO_GETGEO_BIG:
134 {
135 struct hd_big_geometry *loc = (struct hd_big_geometry *) arg;
136 if (!loc) return -EINVAL;
137 if (put_user(raid[minor].geom.heads, (byte *) &loc->heads)) return -EFAULT;
138 if (put_user(raid[minor].geom.sectors, (byte *) &loc->sectors)) return -EFAULT;
139 if (put_user(raid[minor].geom.cylinders, (unsigned int *) &loc->cylinders)) return -EFAULT;
140 if (put_user((unsigned)ataraid_gendisk.part[MINOR(inode->i_rdev)].start_sect,
141 (unsigned long *) &loc->start)) return -EFAULT;
142 return 0;
143 }
144
145
146 case BLKROSET:
147 case BLKROGET:
148 case BLKSSZGET:
149 return blk_ioctl(inode->i_rdev, cmd, arg);
150
151 default:
152 printk("Invalid ioctl \n");
153 return -EINVAL;
154 };
155
156 return 0;
157 }
158
159
partition_map_normal(unsigned long block,unsigned long partition_off,unsigned long partition_size,int stride)160 static unsigned long partition_map_normal(unsigned long block, unsigned long partition_off, unsigned long partition_size, int stride)
161 {
162 return block + partition_off;
163 }
164
silraid0_make_request(request_queue_t * q,int rw,struct buffer_head * bh)165 static int silraid0_make_request (request_queue_t *q, int rw, struct buffer_head * bh)
166 {
167 unsigned long rsect;
168 unsigned long rsect_left,rsect_accum = 0;
169 unsigned long block;
170 unsigned int disk=0,real_disk=0;
171 int i;
172 int device;
173 struct silraid *thisraid;
174
175 rsect = bh->b_rsector;
176
177 /* Ok. We need to modify this sector number to a new disk + new sector number.
178 * If there are disks of different sizes, this gets tricky.
179 * Example with 3 disks (1Gb, 4Gb and 5 GB):
180 * The first 3 Gb of the "RAID" are evenly spread over the 3 disks.
181 * Then things get interesting. The next 2Gb (RAID view) are spread across disk 2 and 3
182 * and the last 1Gb is disk 3 only.
183 *
184 * the way this is solved is like this: We have a list of "cutoff" points where everytime
185 * a disk falls out of the "higher" count, we mark the max sector. So once we pass a cutoff
186 * point, we have to divide by one less.
187 */
188
189 device = (bh->b_rdev >> SHIFT)&MAJOR_MASK;
190 thisraid = &raid[device];
191 if (thisraid->stride==0)
192 thisraid->stride=1;
193
194 /* Partitions need adding of the start sector of the partition to the requested sector */
195
196 rsect = partition_map_normal(rsect, ataraid_gendisk.part[MINOR(bh->b_rdev)].start_sect, ataraid_gendisk.part[MINOR(bh->b_rdev)].nr_sects, thisraid->stride);
197
198 /* Woops we need to split the request to avoid crossing a stride barrier */
199 if ((rsect/thisraid->stride) != ((rsect+(bh->b_size/512)-1)/thisraid->stride)) {
200 return -1;
201 }
202
203 rsect_left = rsect;
204
205 for (i=0;i<8;i++) {
206 if (thisraid->cutoff_disks[i]==0)
207 break;
208 if (rsect > thisraid->cutoff[i]) {
209 /* we're in the wrong area so far */
210 rsect_left -= thisraid->cutoff[i];
211 rsect_accum += thisraid->cutoff[i]/thisraid->cutoff_disks[i];
212 } else {
213 block = rsect_left / thisraid->stride;
214 disk = block % thisraid->cutoff_disks[i];
215 block = (block / thisraid->cutoff_disks[i]) * thisraid->stride;
216 rsect = rsect_accum + (rsect_left % thisraid->stride) + block;
217 break;
218 }
219 }
220
221 for (i=0;i<8;i++) {
222 if ((disk==0) && (thisraid->disk[i].sectors > rsect_accum)) {
223 real_disk = i;
224 break;
225 }
226 if ((disk>0) && (thisraid->disk[i].sectors >= rsect_accum)) {
227 disk--;
228 }
229
230 }
231 disk = real_disk;
232
233
234 /*
235 * The new BH_Lock semantics in ll_rw_blk.c guarantee that this
236 * is the only IO operation happening on this bh.
237 */
238 bh->b_rdev = thisraid->disk[disk].device;
239 bh->b_rsector = rsect;
240
241 /*
242 * Let the main block layer submit the IO and resolve recursion:
243 */
244 return 1;
245 }
246
247 #include "silraid.h"
248
calc_silblock_offset(int major,int minor)249 static unsigned long calc_silblock_offset (int major,int minor)
250 {
251 unsigned long lba = 0, cylinders;
252 kdev_t dev;
253 ide_drive_t *ideinfo;
254
255 dev = MKDEV(major,minor);
256 ideinfo = ide_info_ptr (dev, 0);
257 if (ideinfo==NULL)
258 return 0;
259
260
261 /* last sector second to last cylinder */
262 if (ideinfo->head==0)
263 return 0;
264 if (ideinfo->sect==0)
265 return 0;
266 cylinders = (ideinfo->capacity / (ideinfo->head*ideinfo->sect));
267 lba = (cylinders - 1) * (ideinfo->head*ideinfo->sect);
268 lba = lba - ideinfo->head -1;
269
270 // return 80417215;
271 printk("Guestimating sector %li for superblock\n",lba);
272 return lba;
273
274 }
275
276
277
read_disk_sb(int major,int minor,unsigned char * buffer,int bufsize)278 static int read_disk_sb (int major, int minor, unsigned char *buffer,int bufsize)
279 {
280 int ret = -EINVAL;
281 struct buffer_head *bh = NULL;
282 kdev_t dev = MKDEV(major,minor);
283 unsigned long sb_offset;
284
285 if (blksize_size[major]==NULL) /* device doesn't exist */
286 return -EINVAL;
287
288
289 /*
290 * Calculate the position of the superblock,
291 * it's at first sector of the last cylinder
292 */
293 sb_offset = calc_silblock_offset(major,minor)/8;
294 /* The /8 transforms sectors into 4Kb blocks */
295
296 if (sb_offset==0)
297 return -1;
298
299 set_blocksize (dev, 4096);
300
301 bh = bread (dev, sb_offset, 4096);
302
303 if (bh) {
304 memcpy (buffer, bh->b_data, bufsize);
305 } else {
306 printk(KERN_ERR "silraid: Error reading superblock.\n");
307 goto abort;
308 }
309 ret = 0;
310 abort:
311 if (bh)
312 brelse (bh); return ret;
313 }
314
checksum1(unsigned short * buffer)315 static unsigned short checksum1(unsigned short *buffer)
316 {
317 int i;
318 int sum = 0;
319 for (i=0; i<0x13f/2; i++)
320 sum += buffer[i];
321 return (-sum)&0xFFFF;
322 }
323
324 static int cookie = 0;
325
probedisk(int devindex,int device,int raidlevel)326 static void __init probedisk(int devindex,int device, int raidlevel)
327 {
328 int i;
329 int major, minor;
330 struct signature *superblock;
331 static unsigned char block[4096];
332 struct block_device *bdev;
333
334 if (devlist[devindex].device!=-1) /* already assigned to another array */
335 return;
336
337 major = devlist[devindex].major;
338 minor = devlist[devindex].minor;
339
340 if (read_disk_sb(major,minor,(unsigned char*)&block,sizeof(block)))
341 return;
342
343 superblock = (struct signature*)&block[4096-512];
344
345 if (superblock->unknown[0] != 'Z') /* Need better check here */
346 return;
347
348 if (superblock->checksum1 != checksum1((unsigned short*)superblock))
349 return;
350
351
352
353 if (superblock->raidlevel!=raidlevel) /* different raidlevel */
354 return;
355
356 /* This looks evil. But basically, we have to search for our adapternumber
357 in the arraydefinition, both of which are in the superblock */
358 i = superblock->disk_in_set;
359
360 bdev = bdget(MKDEV(major,minor));
361 if (bdev && blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_RAW) == 0) {
362 raid[device].disk[i].bdev = bdev;
363 }
364 raid[device].disk[i].device = MKDEV(major,minor);
365 raid[device].disk[i].sectors = superblock->thisdisk_sectors;
366 raid[device].stride = superblock->raid0_sectors_per_stride;
367 raid[device].disks = superblock->disks_in_set;
368 raid[device].sectors = superblock->array_sectors;
369 raid[device].geom.heads = 255;
370 raid[device].geom.sectors = 63;
371 raid[device].geom.cylinders = raid[device].sectors / raid[device].geom.heads / raid[device].geom.sectors;
372
373 devlist[devindex].device=device;
374
375 }
376
fill_cutoff(int device)377 static void __init fill_cutoff(int device)
378 {
379 int i,j;
380 unsigned long smallest;
381 unsigned long bar;
382 int count;
383
384 bar = 0;
385 for (i=0;i<8;i++) {
386 smallest = ~0;
387 for (j=0;j<8;j++)
388 if ((raid[device].disk[j].sectors < smallest) && (raid[device].disk[j].sectors>bar))
389 smallest = raid[device].disk[j].sectors;
390 count = 0;
391 for (j=0;j<8;j++)
392 if (raid[device].disk[j].sectors >= smallest)
393 count++;
394
395 smallest = smallest * count;
396 bar = smallest;
397 raid[device].cutoff[i] = smallest;
398 raid[device].cutoff_disks[i] = count;
399 }
400 }
401
silraid_init_one(int device,int raidlevel)402 static __init int silraid_init_one(int device,int raidlevel)
403 {
404 int i, count;
405
406 for (i=0; i<14; i++)
407 probedisk(i, device, raidlevel);
408
409 if (raidlevel==0)
410 fill_cutoff(device);
411
412 /* Initialize the gendisk structure */
413
414 ataraid_register_disk(device,raid[device].sectors);
415
416 count=0;
417
418 for (i=0;i<8;i++) {
419 if (raid[device].disk[i].device!=0) {
420 printk(KERN_INFO "Drive %i is %li Mb (%i / %i) \n",
421 i,raid[device].disk[i].sectors/2048,MAJOR(raid[device].disk[i].device),MINOR(raid[device].disk[i].device));
422 count++;
423 }
424 }
425 if (count) {
426 printk(KERN_INFO "Raid%i array consists of %i drives. \n",raidlevel,count);
427 return 0;
428 } else {
429 return -ENODEV;
430 }
431 }
432
silraid_init(void)433 static __init int silraid_init(void)
434 {
435 int retval, device, count = 0;
436
437 do {
438 cookie = 0;
439 device=ataraid_get_device(&silraid0_ops);
440 if (device<0)
441 break;
442 retval = silraid_init_one(device,0);
443 if (retval) {
444 ataraid_release_device(device);
445 break;
446 } else {
447 count++;
448 }
449 } while (1);
450
451 if (count) {
452 printk(KERN_INFO "driver for Silicon Image(tm) Medley(tm) hardware version 0.0.1\n");
453 return 0;
454 }
455 printk(KERN_DEBUG "driver for Silicon Image(tm) Medley(tm) hardware version 0.0.1: No raid array found\n");
456 return -ENODEV;
457 }
458
silraid_exit(void)459 static void __exit silraid_exit (void)
460 {
461 int i,device;
462 for (device = 0; device<16; device++) {
463 for (i=0;i<8;i++) {
464 struct block_device *bdev = raid[device].disk[i].bdev;
465 raid[device].disk[i].bdev = NULL;
466 if (bdev)
467 blkdev_put(bdev, BDEV_RAW);
468 }
469 if (raid[device].sectors)
470 ataraid_release_device(device);
471 }
472 }
473
silraid_open(struct inode * inode,struct file * filp)474 static int silraid_open(struct inode * inode, struct file * filp)
475 {
476 MOD_INC_USE_COUNT;
477 return 0;
478 }
silraid_release(struct inode * inode,struct file * filp)479 static int silraid_release(struct inode * inode, struct file * filp)
480 {
481 MOD_DEC_USE_COUNT;
482 return 0;
483 }
484
485 module_init(silraid_init);
486 module_exit(silraid_exit);
487 MODULE_LICENSE("GPL and additional rights");
488