Blame view

drivers/md/dm-exception-store.c 6.54 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
   * Copyright (C) 2001-2002 Sistina Software (UK) Limited.
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
3
   * Copyright (C) 2006-2008 Red Hat GmbH
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
   *
   * This file is released under the GPL.
   */
aea53d92f   Jonathan Brassow   dm snapshot: sepa...
7
  #include "dm-exception-store.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8

fee1998e9   Jonathan Brassow   dm snapshot: move...
9
  #include <linux/ctype.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
  #include <linux/mm.h>
  #include <linux/pagemap.h>
  #include <linux/vmalloc.h>
056075c76   Paul Gortmaker   md: Add module.h ...
13
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15

4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
16
  #define DM_MSG_PREFIX "snapshot exception stores"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17

493df71c6   Jonathan Brassow   dm exception stor...
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
  static LIST_HEAD(_exception_store_types);
  static DEFINE_SPINLOCK(_lock);
  
  static struct dm_exception_store_type *__find_exception_store_type(const char *name)
  {
  	struct dm_exception_store_type *type;
  
  	list_for_each_entry(type, &_exception_store_types, list)
  		if (!strcmp(name, type->name))
  			return type;
  
  	return NULL;
  }
  
  static struct dm_exception_store_type *_get_exception_store_type(const char *name)
  {
  	struct dm_exception_store_type *type;
  
  	spin_lock(&_lock);
  
  	type = __find_exception_store_type(name);
  
  	if (type && !try_module_get(type->module))
  		type = NULL;
  
  	spin_unlock(&_lock);
  
  	return type;
  }
  
  /*
   * get_type
   * @type_name
   *
   * Attempt to retrieve the dm_exception_store_type by name.  If not already
   * available, attempt to load the appropriate module.
   *
   * Exstore modules are named "dm-exstore-" followed by the 'type_name'.
   * Modules may contain multiple types.
   * This function will first try the module "dm-exstore-<type_name>",
   * then truncate 'type_name' on the last '-' and try again.
   *
   * For example, if type_name was "clustered-shared", it would search
   * 'dm-exstore-clustered-shared' then 'dm-exstore-clustered'.
   *
   * 'dm-exception-store-<type_name>' is too long of a name in my
   * opinion, which is why I've chosen to have the files
   * containing exception store implementations be 'dm-exstore-<type_name>'.
   * If you want your module to be autoloaded, you will follow this
   * naming convention.
   *
   * Returns: dm_exception_store_type* on success, NULL on failure
   */
  static struct dm_exception_store_type *get_type(const char *type_name)
  {
  	char *p, *type_name_dup;
  	struct dm_exception_store_type *type;
  
  	type = _get_exception_store_type(type_name);
  	if (type)
  		return type;
  
  	type_name_dup = kstrdup(type_name, GFP_KERNEL);
  	if (!type_name_dup) {
  		DMERR("No memory left to attempt load for \"%s\"", type_name);
  		return NULL;
  	}
  
  	while (request_module("dm-exstore-%s", type_name_dup) ||
  	       !(type = _get_exception_store_type(type_name))) {
  		p = strrchr(type_name_dup, '-');
  		if (!p)
  			break;
  		p[0] = '\0';
  	}
  
  	if (!type)
  		DMWARN("Module for exstore type \"%s\" not found.", type_name);
  
  	kfree(type_name_dup);
  
  	return type;
  }
  
  static void put_type(struct dm_exception_store_type *type)
  {
  	spin_lock(&_lock);
  	module_put(type->module);
  	spin_unlock(&_lock);
  }
  
  int dm_exception_store_type_register(struct dm_exception_store_type *type)
  {
  	int r = 0;
  
  	spin_lock(&_lock);
  	if (!__find_exception_store_type(type->name))
  		list_add(&type->list, &_exception_store_types);
  	else
  		r = -EEXIST;
  	spin_unlock(&_lock);
  
  	return r;
  }
  EXPORT_SYMBOL(dm_exception_store_type_register);
  
  int dm_exception_store_type_unregister(struct dm_exception_store_type *type)
  {
  	spin_lock(&_lock);
  
  	if (!__find_exception_store_type(type->name)) {
  		spin_unlock(&_lock);
  		return -EINVAL;
  	}
  
  	list_del(&type->list);
  
  	spin_unlock(&_lock);
  
  	return 0;
  }
  EXPORT_SYMBOL(dm_exception_store_type_unregister);
fee1998e9   Jonathan Brassow   dm snapshot: move...
140
141
142
143
144
145
146
  static int set_chunk_size(struct dm_exception_store *store,
  			  const char *chunk_size_arg, char **error)
  {
  	unsigned long chunk_size_ulong;
  	char *value;
  
  	chunk_size_ulong = simple_strtoul(chunk_size_arg, &value, 10);
df96eee67   Mikulas Patocka   dm snapshot: use ...
147
148
  	if (*chunk_size_arg == '\0' || *value != '\0' ||
  	    chunk_size_ulong > UINT_MAX) {
fee1998e9   Jonathan Brassow   dm snapshot: move...
149
150
151
152
153
154
155
156
  		*error = "Invalid chunk size";
  		return -EINVAL;
  	}
  
  	if (!chunk_size_ulong) {
  		store->chunk_size = store->chunk_mask = store->chunk_shift = 0;
  		return 0;
  	}
df96eee67   Mikulas Patocka   dm snapshot: use ...
157
158
  	return dm_exception_store_set_chunk_size(store,
  						 (unsigned) chunk_size_ulong,
2defcc3fb   Mikulas Patocka   dm exception stor...
159
160
161
162
  						 error);
  }
  
  int dm_exception_store_set_chunk_size(struct dm_exception_store *store,
df96eee67   Mikulas Patocka   dm snapshot: use ...
163
  				      unsigned chunk_size,
2defcc3fb   Mikulas Patocka   dm exception stor...
164
165
  				      char **error)
  {
fee1998e9   Jonathan Brassow   dm snapshot: move...
166
  	/* Check chunk_size is a power of 2 */
df96eee67   Mikulas Patocka   dm snapshot: use ...
167
  	if (!is_power_of_2(chunk_size)) {
fee1998e9   Jonathan Brassow   dm snapshot: move...
168
169
170
171
172
  		*error = "Chunk size is not a power of 2";
  		return -EINVAL;
  	}
  
  	/* Validate the chunk size against the device block size */
fc56f6fbc   Mike Snitzer   dm snapshot: move...
173
  	if (chunk_size %
c24110450   Mikulas Patocka   dm snapshot: test...
174
175
176
  	    (bdev_logical_block_size(dm_snap_cow(store->snap)->bdev) >> 9) ||
  	    chunk_size %
  	    (bdev_logical_block_size(dm_snap_origin(store->snap)->bdev) >> 9)) {
fee1998e9   Jonathan Brassow   dm snapshot: move...
177
178
179
  		*error = "Chunk size is not a multiple of device blocksize";
  		return -EINVAL;
  	}
df96eee67   Mikulas Patocka   dm snapshot: use ...
180
  	if (chunk_size > INT_MAX >> SECTOR_SHIFT) {
ae0b7448e   Mikulas Patocka   dm snapshot: fix ...
181
182
183
  		*error = "Chunk size is too high";
  		return -EINVAL;
  	}
df96eee67   Mikulas Patocka   dm snapshot: use ...
184
185
186
  	store->chunk_size = chunk_size;
  	store->chunk_mask = chunk_size - 1;
  	store->chunk_shift = ffs(chunk_size) - 1;
fee1998e9   Jonathan Brassow   dm snapshot: move...
187
188
189
190
191
  
  	return 0;
  }
  
  int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
fc56f6fbc   Mike Snitzer   dm snapshot: move...
192
  			      struct dm_snapshot *snap,
fee1998e9   Jonathan Brassow   dm snapshot: move...
193
  			      unsigned *args_used,
493df71c6   Jonathan Brassow   dm exception stor...
194
195
196
  			      struct dm_exception_store **store)
  {
  	int r = 0;
874d2f61d   Milan Broz   dm exception stor...
197
  	struct dm_exception_store_type *type = NULL;
493df71c6   Jonathan Brassow   dm exception stor...
198
  	struct dm_exception_store *tmp_store;
fee1998e9   Jonathan Brassow   dm snapshot: move...
199
  	char persistent;
fc56f6fbc   Mike Snitzer   dm snapshot: move...
200
  	if (argc < 2) {
fee1998e9   Jonathan Brassow   dm snapshot: move...
201
202
203
  		ti->error = "Insufficient exception store arguments";
  		return -EINVAL;
  	}
493df71c6   Jonathan Brassow   dm exception stor...
204
205
  
  	tmp_store = kmalloc(sizeof(*tmp_store), GFP_KERNEL);
fee1998e9   Jonathan Brassow   dm snapshot: move...
206
207
  	if (!tmp_store) {
  		ti->error = "Exception store allocation failed";
493df71c6   Jonathan Brassow   dm exception stor...
208
  		return -ENOMEM;
fee1998e9   Jonathan Brassow   dm snapshot: move...
209
  	}
493df71c6   Jonathan Brassow   dm exception stor...
210

fc56f6fbc   Mike Snitzer   dm snapshot: move...
211
  	persistent = toupper(*argv[0]);
874d2f61d   Milan Broz   dm exception stor...
212
213
214
215
216
  	if (persistent == 'P')
  		type = get_type("P");
  	else if (persistent == 'N')
  		type = get_type("N");
  	else {
fee1998e9   Jonathan Brassow   dm snapshot: move...
217
  		ti->error = "Persistent flag is not P or N";
613978f87   Julia Lawall   dm exception stor...
218
219
  		r = -EINVAL;
  		goto bad_type;
493df71c6   Jonathan Brassow   dm exception stor...
220
  	}
fee1998e9   Jonathan Brassow   dm snapshot: move...
221
222
223
224
225
  	if (!type) {
  		ti->error = "Exception store type not recognised";
  		r = -EINVAL;
  		goto bad_type;
  	}
493df71c6   Jonathan Brassow   dm exception stor...
226
  	tmp_store->type = type;
fc56f6fbc   Mike Snitzer   dm snapshot: move...
227
  	tmp_store->snap = snap;
493df71c6   Jonathan Brassow   dm exception stor...
228

fc56f6fbc   Mike Snitzer   dm snapshot: move...
229
  	r = set_chunk_size(tmp_store, argv[1], &ti->error);
fee1998e9   Jonathan Brassow   dm snapshot: move...
230
  	if (r)
fc56f6fbc   Mike Snitzer   dm snapshot: move...
231
  		goto bad;
49beb2b87   Jonathan Brassow   dm exception stor...
232

493df71c6   Jonathan Brassow   dm exception stor...
233
234
  	r = type->ctr(tmp_store, 0, NULL);
  	if (r) {
fee1998e9   Jonathan Brassow   dm snapshot: move...
235
  		ti->error = "Exception store type constructor failed";
fc56f6fbc   Mike Snitzer   dm snapshot: move...
236
  		goto bad;
493df71c6   Jonathan Brassow   dm exception stor...
237
  	}
fc56f6fbc   Mike Snitzer   dm snapshot: move...
238
  	*args_used = 2;
493df71c6   Jonathan Brassow   dm exception stor...
239
240
  	*store = tmp_store;
  	return 0;
fee1998e9   Jonathan Brassow   dm snapshot: move...
241

fc56f6fbc   Mike Snitzer   dm snapshot: move...
242
  bad:
fee1998e9   Jonathan Brassow   dm snapshot: move...
243
244
245
246
  	put_type(type);
  bad_type:
  	kfree(tmp_store);
  	return r;
493df71c6   Jonathan Brassow   dm exception stor...
247
248
249
250
251
252
253
254
255
256
  }
  EXPORT_SYMBOL(dm_exception_store_create);
  
  void dm_exception_store_destroy(struct dm_exception_store *store)
  {
  	store->type->dtr(store);
  	put_type(store->type);
  	kfree(store);
  }
  EXPORT_SYMBOL(dm_exception_store_destroy);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
257
  int dm_exception_store_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
  {
  	int r;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260

4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
261
262
263
264
  	r = dm_transient_snapshot_init();
  	if (r) {
  		DMERR("Unable to register transient exception store type.");
  		goto transient_fail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
  	}
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
266
267
268
269
  	r = dm_persistent_snapshot_init();
  	if (r) {
  		DMERR("Unable to register persistent exception store type");
  		goto persistent_fail;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
272
  	}
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273

4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
274
275
276
277
  persistent_fail:
  	dm_persistent_snapshot_exit();
  transient_fail:
  	return r;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
  }
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
279
  void dm_exception_store_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  {
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
281
282
  	dm_persistent_snapshot_exit();
  	dm_transient_snapshot_exit();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
  }