6f0a7923547b6690aec7b3b024e55741c04b1c19
[linux-flexiantxendom0-3.2.10.git] / drivers / md / linear.c
1 /*
2    linear.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
7    Linear mode management functions.
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2, or (at your option)
12    any later version.
13    
14    You should have received a copy of the GNU General Public License
15    (for example /usr/src/linux/COPYING); if not, write to the Free
16    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
17 */
18
19 #include <linux/module.h>
20
21 #include <linux/raid/md.h>
22 #include <linux/slab.h>
23
24 #include <linux/raid/linear.h>
25
26 #define MAJOR_NR MD_MAJOR
27 #define MD_DRIVER
28 #define MD_PERSONALITY
29
30 static int linear_run (mddev_t *mddev)
31 {
32         linear_conf_t *conf;
33         struct linear_hash *table;
34         mdk_rdev_t *rdev;
35         int size, i, j, nb_zone;
36         unsigned int curr_offset;
37
38         MOD_INC_USE_COUNT;
39
40         conf = kmalloc (sizeof (*conf), GFP_KERNEL);
41         if (!conf)
42                 goto out;
43         mddev->private = conf;
44
45         if (md_check_ordering(mddev)) {
46                 printk("linear: disks are not ordered, aborting!\n");
47                 goto out;
48         }
49         /*
50          * Find the smallest device.
51          */
52
53         conf->smallest = NULL;
54         curr_offset = 0;
55         ITERATE_RDEV_ORDERED(mddev,rdev,j) {
56                 dev_info_t *disk = conf->disks + j;
57
58                 disk->dev = rdev->dev;
59                 disk->size = rdev->size;
60                 disk->offset = curr_offset;
61
62                 curr_offset += disk->size;
63
64                 if (!conf->smallest || (disk->size < conf->smallest->size))
65                         conf->smallest = disk;
66         }
67
68         nb_zone = conf->nr_zones =
69                 md_size[mdidx(mddev)] / conf->smallest->size +
70                 ((md_size[mdidx(mddev)] % conf->smallest->size) ? 1 : 0);
71   
72         conf->hash_table = kmalloc (sizeof (struct linear_hash) * nb_zone,
73                                         GFP_KERNEL);
74         if (!conf->hash_table)
75                 goto out;
76
77         /*
78          * Here we generate the linear hash table
79          */
80         table = conf->hash_table;
81         i = 0;
82         size = 0;
83         for (j = 0; j < mddev->nb_dev; j++) {
84                 dev_info_t *disk = conf->disks + j;
85
86                 if (size < 0) {
87                         table[-1].dev1 = disk;
88                 }
89                 size += disk->size;
90
91                 while (size>0) {
92                         table->dev0 = disk;
93                         table->dev1 = NULL;
94                         size -= conf->smallest->size;
95                         table++;
96                 }
97         }
98         if (table-conf->hash_table != nb_zone)
99                 BUG();
100
101         return 0;
102
103 out:
104         if (conf)
105                 kfree(conf);
106         MOD_DEC_USE_COUNT;
107         return 1;
108 }
109
110 static int linear_stop (mddev_t *mddev)
111 {
112         linear_conf_t *conf = mddev_to_conf(mddev);
113   
114         kfree(conf->hash_table);
115         kfree(conf);
116
117         MOD_DEC_USE_COUNT;
118
119         return 0;
120 }
121
122 static int linear_make_request (mddev_t *mddev,
123                         int rw, struct buffer_head * bh)
124 {
125         linear_conf_t *conf = mddev_to_conf(mddev);
126         struct linear_hash *hash;
127         dev_info_t *tmp_dev;
128         long block;
129
130         block = bh->b_rsector >> 1;
131         hash = conf->hash_table + (block / conf->smallest->size);
132   
133         if (block >= (hash->dev0->size + hash->dev0->offset)) {
134                 if (!hash->dev1) {
135                         printk ("linear_make_request : hash->dev1==NULL for block %ld\n",
136                                                 block);
137                         buffer_IO_error(bh);
138                         return 0;
139                 }
140                 tmp_dev = hash->dev1;
141         } else
142                 tmp_dev = hash->dev0;
143     
144         if (block >= (tmp_dev->size + tmp_dev->offset)
145                                 || block < tmp_dev->offset) {
146                 printk ("linear_make_request: Block %ld out of bounds on dev %s size %ld offset %ld\n", block, kdevname(tmp_dev->dev), tmp_dev->size, tmp_dev->offset);
147                 buffer_IO_error(bh);
148                 return 0;
149         }
150         bh->b_rdev = tmp_dev->dev;
151         bh->b_rsector = bh->b_rsector - (tmp_dev->offset << 1);
152
153         return 1;
154 }
155
156 static int linear_status (char *page, mddev_t *mddev)
157 {
158         int sz = 0;
159
160 #undef MD_DEBUG
161 #ifdef MD_DEBUG
162         int j;
163         linear_conf_t *conf = mddev_to_conf(mddev);
164   
165         sz += sprintf(page+sz, "      ");
166         for (j = 0; j < conf->nr_zones; j++)
167         {
168                 sz += sprintf(page+sz, "[%s",
169                         partition_name(conf->hash_table[j].dev0->dev));
170
171                 if (conf->hash_table[j].dev1)
172                         sz += sprintf(page+sz, "/%s] ",
173                           partition_name(conf->hash_table[j].dev1->dev));
174                 else
175                         sz += sprintf(page+sz, "] ");
176         }
177         sz += sprintf(page+sz, "\n");
178 #endif
179         sz += sprintf(page+sz, " %dk rounding", mddev->param.chunk_size/1024);
180         return sz;
181 }
182
183
184 static mdk_personality_t linear_personality=
185 {
186         name:           "linear",
187         make_request:   linear_make_request,
188         run:            linear_run,
189         stop:           linear_stop,
190         status:         linear_status,
191 };
192
193 static int md__init linear_init (void)
194 {
195         return register_md_personality (LINEAR, &linear_personality);
196 }
197
198 static void linear_exit (void)
199 {
200         unregister_md_personality (LINEAR);
201 }
202
203
204 module_init(linear_init);
205 module_exit(linear_exit);
206 MODULE_LICENSE("GPL");