File: /usr/src/linux/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