diff options
author | Hongjie Yang <hongjie@us.ibm.com> | 2008-10-10 15:33:21 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-10-10 15:33:57 -0400 |
commit | b2300b9efe1b8174833e17f37e975c9da00c388a (patch) | |
tree | b3413247d703aee9bd769ff09a782b679aba32cc /drivers/s390/block | |
parent | 753c4dd6a2fa2af81f5d809d610d29f2d9dd9bc1 (diff) |
[S390] dcssblk: add >2G DCSSs support and stacked contiguous DCSSs support.
The DCSS block device driver is modified to add >2G DCSSs support and
allow a DCSS block device to map to a set of contiguous DCSSs. The
extmem code is also modified to use new Diagnose x'64' subcodes for
>2G DCSSs.
Signed-off-by: Hongjie Yang <hongjie@us.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/block')
-rw-r--r-- | drivers/s390/block/dcssblk.c | 515 |
1 files changed, 384 insertions, 131 deletions
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index ea4272c8c677..a7ff167d5b81 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c | |||
@@ -31,7 +31,6 @@ | |||
31 | #define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x) | 31 | #define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x) |
32 | #define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x) | 32 | #define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x) |
33 | 33 | ||
34 | |||
35 | static int dcssblk_open(struct inode *inode, struct file *filp); | 34 | static int dcssblk_open(struct inode *inode, struct file *filp); |
36 | static int dcssblk_release(struct inode *inode, struct file *filp); | 35 | static int dcssblk_release(struct inode *inode, struct file *filp); |
37 | static int dcssblk_make_request(struct request_queue *q, struct bio *bio); | 36 | static int dcssblk_make_request(struct request_queue *q, struct bio *bio); |
@@ -48,6 +47,30 @@ static struct block_device_operations dcssblk_devops = { | |||
48 | .direct_access = dcssblk_direct_access, | 47 | .direct_access = dcssblk_direct_access, |
49 | }; | 48 | }; |
50 | 49 | ||
50 | struct dcssblk_dev_info { | ||
51 | struct list_head lh; | ||
52 | struct device dev; | ||
53 | char segment_name[BUS_ID_SIZE]; | ||
54 | atomic_t use_count; | ||
55 | struct gendisk *gd; | ||
56 | unsigned long start; | ||
57 | unsigned long end; | ||
58 | int segment_type; | ||
59 | unsigned char save_pending; | ||
60 | unsigned char is_shared; | ||
61 | struct request_queue *dcssblk_queue; | ||
62 | int num_of_segments; | ||
63 | struct list_head seg_list; | ||
64 | }; | ||
65 | |||
66 | struct segment_info { | ||
67 | struct list_head lh; | ||
68 | char segment_name[BUS_ID_SIZE]; | ||
69 | unsigned long start; | ||
70 | unsigned long end; | ||
71 | int segment_type; | ||
72 | }; | ||
73 | |||
51 | static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf, | 74 | static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf, |
52 | size_t count); | 75 | size_t count); |
53 | static ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf, | 76 | static ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf, |
@@ -58,30 +81,20 @@ static ssize_t dcssblk_save_show(struct device *dev, struct device_attribute *at | |||
58 | static ssize_t dcssblk_shared_store(struct device * dev, struct device_attribute *attr, const char * buf, | 81 | static ssize_t dcssblk_shared_store(struct device * dev, struct device_attribute *attr, const char * buf, |
59 | size_t count); | 82 | size_t count); |
60 | static ssize_t dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf); | 83 | static ssize_t dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf); |
84 | static ssize_t dcssblk_seglist_show(struct device *dev, | ||
85 | struct device_attribute *attr, | ||
86 | char *buf); | ||
61 | 87 | ||
62 | static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store); | 88 | static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store); |
63 | static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store); | 89 | static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store); |
64 | static DEVICE_ATTR(save, S_IWUSR | S_IRUGO, dcssblk_save_show, | 90 | static DEVICE_ATTR(save, S_IWUSR | S_IRUSR, dcssblk_save_show, |
65 | dcssblk_save_store); | 91 | dcssblk_save_store); |
66 | static DEVICE_ATTR(shared, S_IWUSR | S_IRUGO, dcssblk_shared_show, | 92 | static DEVICE_ATTR(shared, S_IWUSR | S_IRUSR, dcssblk_shared_show, |
67 | dcssblk_shared_store); | 93 | dcssblk_shared_store); |
94 | static DEVICE_ATTR(seglist, S_IRUSR, dcssblk_seglist_show, NULL); | ||
68 | 95 | ||
69 | static struct device *dcssblk_root_dev; | 96 | static struct device *dcssblk_root_dev; |
70 | 97 | ||
71 | struct dcssblk_dev_info { | ||
72 | struct list_head lh; | ||
73 | struct device dev; | ||
74 | char segment_name[BUS_ID_SIZE]; | ||
75 | atomic_t use_count; | ||
76 | struct gendisk *gd; | ||
77 | unsigned long start; | ||
78 | unsigned long end; | ||
79 | int segment_type; | ||
80 | unsigned char save_pending; | ||
81 | unsigned char is_shared; | ||
82 | struct request_queue *dcssblk_queue; | ||
83 | }; | ||
84 | |||
85 | static LIST_HEAD(dcssblk_devices); | 98 | static LIST_HEAD(dcssblk_devices); |
86 | static struct rw_semaphore dcssblk_devices_sem; | 99 | static struct rw_semaphore dcssblk_devices_sem; |
87 | 100 | ||
@@ -91,8 +104,15 @@ static struct rw_semaphore dcssblk_devices_sem; | |||
91 | static void | 104 | static void |
92 | dcssblk_release_segment(struct device *dev) | 105 | dcssblk_release_segment(struct device *dev) |
93 | { | 106 | { |
94 | PRINT_DEBUG("segment release fn called for %s\n", dev_name(dev)); | 107 | struct dcssblk_dev_info *dev_info; |
95 | kfree(container_of(dev, struct dcssblk_dev_info, dev)); | 108 | struct segment_info *entry, *temp; |
109 | |||
110 | dev_info = container_of(dev, struct dcssblk_dev_info, dev); | ||
111 | list_for_each_entry_safe(entry, temp, &dev_info->seg_list, lh) { | ||
112 | list_del(&entry->lh); | ||
113 | kfree(entry); | ||
114 | } | ||
115 | kfree(dev_info); | ||
96 | module_put(THIS_MODULE); | 116 | module_put(THIS_MODULE); |
97 | } | 117 | } |
98 | 118 | ||
@@ -142,6 +162,169 @@ dcssblk_get_device_by_name(char *name) | |||
142 | return NULL; | 162 | return NULL; |
143 | } | 163 | } |
144 | 164 | ||
165 | /* | ||
166 | * get the struct segment_info from seg_list | ||
167 | * for the given name. | ||
168 | * down_read(&dcssblk_devices_sem) must be held. | ||
169 | */ | ||
170 | static struct segment_info * | ||
171 | dcssblk_get_segment_by_name(char *name) | ||
172 | { | ||
173 | struct dcssblk_dev_info *dev_info; | ||
174 | struct segment_info *entry; | ||
175 | |||
176 | list_for_each_entry(dev_info, &dcssblk_devices, lh) { | ||
177 | list_for_each_entry(entry, &dev_info->seg_list, lh) { | ||
178 | if (!strcmp(name, entry->segment_name)) | ||
179 | return entry; | ||
180 | } | ||
181 | } | ||
182 | return NULL; | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | * get the highest address of the multi-segment block. | ||
187 | */ | ||
188 | static unsigned long | ||
189 | dcssblk_find_highest_addr(struct dcssblk_dev_info *dev_info) | ||
190 | { | ||
191 | unsigned long highest_addr; | ||
192 | struct segment_info *entry; | ||
193 | |||
194 | highest_addr = 0; | ||
195 | list_for_each_entry(entry, &dev_info->seg_list, lh) { | ||
196 | if (highest_addr < entry->end) | ||
197 | highest_addr = entry->end; | ||
198 | } | ||
199 | return highest_addr; | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * get the lowest address of the multi-segment block. | ||
204 | */ | ||
205 | static unsigned long | ||
206 | dcssblk_find_lowest_addr(struct dcssblk_dev_info *dev_info) | ||
207 | { | ||
208 | int set_first; | ||
209 | unsigned long lowest_addr; | ||
210 | struct segment_info *entry; | ||
211 | |||
212 | set_first = 0; | ||
213 | lowest_addr = 0; | ||
214 | list_for_each_entry(entry, &dev_info->seg_list, lh) { | ||
215 | if (set_first == 0) { | ||
216 | lowest_addr = entry->start; | ||
217 | set_first = 1; | ||
218 | } else { | ||
219 | if (lowest_addr > entry->start) | ||
220 | lowest_addr = entry->start; | ||
221 | } | ||
222 | } | ||
223 | return lowest_addr; | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | * Check continuity of segments. | ||
228 | */ | ||
229 | static int | ||
230 | dcssblk_is_continuous(struct dcssblk_dev_info *dev_info) | ||
231 | { | ||
232 | int i, j, rc; | ||
233 | struct segment_info *sort_list, *entry, temp; | ||
234 | |||
235 | if (dev_info->num_of_segments <= 1) | ||
236 | return 0; | ||
237 | |||
238 | sort_list = kzalloc( | ||
239 | sizeof(struct segment_info) * dev_info->num_of_segments, | ||
240 | GFP_KERNEL); | ||
241 | if (sort_list == NULL) | ||
242 | return -ENOMEM; | ||
243 | i = 0; | ||
244 | list_for_each_entry(entry, &dev_info->seg_list, lh) { | ||
245 | memcpy(&sort_list[i], entry, sizeof(struct segment_info)); | ||
246 | i++; | ||
247 | } | ||
248 | |||
249 | /* sort segments */ | ||
250 | for (i = 0; i < dev_info->num_of_segments; i++) | ||
251 | for (j = 0; j < dev_info->num_of_segments; j++) | ||
252 | if (sort_list[j].start > sort_list[i].start) { | ||
253 | memcpy(&temp, &sort_list[i], | ||
254 | sizeof(struct segment_info)); | ||
255 | memcpy(&sort_list[i], &sort_list[j], | ||
256 | sizeof(struct segment_info)); | ||
257 | memcpy(&sort_list[j], &temp, | ||
258 | sizeof(struct segment_info)); | ||
259 | } | ||
260 | |||
261 | /* check continuity */ | ||
262 | for (i = 0; i < dev_info->num_of_segments - 1; i++) { | ||
263 | if ((sort_list[i].end + 1) != sort_list[i+1].start) { | ||
264 | PRINT_ERR("Segment %s is not contiguous with " | ||
265 | "segment %s\n", | ||
266 | sort_list[i].segment_name, | ||
267 | sort_list[i+1].segment_name); | ||
268 | rc = -EINVAL; | ||
269 | goto out; | ||
270 | } | ||
271 | /* EN and EW are allowed in a block device */ | ||
272 | if (sort_list[i].segment_type != sort_list[i+1].segment_type) { | ||
273 | if (!(sort_list[i].segment_type & SEGMENT_EXCLUSIVE) || | ||
274 | (sort_list[i].segment_type == SEG_TYPE_ER) || | ||
275 | !(sort_list[i+1].segment_type & | ||
276 | SEGMENT_EXCLUSIVE) || | ||
277 | (sort_list[i+1].segment_type == SEG_TYPE_ER)) { | ||
278 | PRINT_ERR("Segment %s has different type from " | ||
279 | "segment %s\n", | ||
280 | sort_list[i].segment_name, | ||
281 | sort_list[i+1].segment_name); | ||
282 | rc = -EINVAL; | ||
283 | goto out; | ||
284 | } | ||
285 | } | ||
286 | } | ||
287 | rc = 0; | ||
288 | out: | ||
289 | kfree(sort_list); | ||
290 | return rc; | ||
291 | } | ||
292 | |||
293 | /* | ||
294 | * Load a segment | ||
295 | */ | ||
296 | static int | ||
297 | dcssblk_load_segment(char *name, struct segment_info **seg_info) | ||
298 | { | ||
299 | int rc; | ||
300 | |||
301 | /* already loaded? */ | ||
302 | down_read(&dcssblk_devices_sem); | ||
303 | *seg_info = dcssblk_get_segment_by_name(name); | ||
304 | up_read(&dcssblk_devices_sem); | ||
305 | if (*seg_info != NULL) | ||
306 | return -EEXIST; | ||
307 | |||
308 | /* get a struct segment_info */ | ||
309 | *seg_info = kzalloc(sizeof(struct segment_info), GFP_KERNEL); | ||
310 | if (*seg_info == NULL) | ||
311 | return -ENOMEM; | ||
312 | |||
313 | strcpy((*seg_info)->segment_name, name); | ||
314 | |||
315 | /* load the segment */ | ||
316 | rc = segment_load(name, SEGMENT_SHARED, | ||
317 | &(*seg_info)->start, &(*seg_info)->end); | ||
318 | if (rc < 0) { | ||
319 | segment_warning(rc, (*seg_info)->segment_name); | ||
320 | kfree(*seg_info); | ||
321 | } else { | ||
322 | INIT_LIST_HEAD(&(*seg_info)->lh); | ||
323 | (*seg_info)->segment_type = rc; | ||
324 | } | ||
325 | return rc; | ||
326 | } | ||
327 | |||
145 | static void dcssblk_unregister_callback(struct device *dev) | 328 | static void dcssblk_unregister_callback(struct device *dev) |
146 | { | 329 | { |
147 | device_unregister(dev); | 330 | device_unregister(dev); |
@@ -165,6 +348,7 @@ static ssize_t | |||
165 | dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) | 348 | dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) |
166 | { | 349 | { |
167 | struct dcssblk_dev_info *dev_info; | 350 | struct dcssblk_dev_info *dev_info; |
351 | struct segment_info *entry, *temp; | ||
168 | int rc; | 352 | int rc; |
169 | 353 | ||
170 | if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) | 354 | if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) |
@@ -172,46 +356,46 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch | |||
172 | down_write(&dcssblk_devices_sem); | 356 | down_write(&dcssblk_devices_sem); |
173 | dev_info = container_of(dev, struct dcssblk_dev_info, dev); | 357 | dev_info = container_of(dev, struct dcssblk_dev_info, dev); |
174 | if (atomic_read(&dev_info->use_count)) { | 358 | if (atomic_read(&dev_info->use_count)) { |
175 | PRINT_ERR("share: segment %s is busy!\n", | ||
176 | dev_info->segment_name); | ||
177 | rc = -EBUSY; | 359 | rc = -EBUSY; |
178 | goto out; | 360 | goto out; |
179 | } | 361 | } |
180 | if (inbuf[0] == '1') { | 362 | if (inbuf[0] == '1') { |
181 | // reload segment in shared mode | 363 | /* reload segments in shared mode */ |
182 | rc = segment_modify_shared(dev_info->segment_name, | 364 | list_for_each_entry(entry, &dev_info->seg_list, lh) { |
183 | SEGMENT_SHARED); | 365 | rc = segment_modify_shared(entry->segment_name, |
184 | if (rc < 0) { | 366 | SEGMENT_SHARED); |
185 | BUG_ON(rc == -EINVAL); | 367 | if (rc < 0) { |
186 | if (rc != -EAGAIN) | 368 | BUG_ON(rc == -EINVAL); |
187 | goto removeseg; | 369 | if (rc != -EAGAIN) |
188 | } else { | 370 | goto removeseg; |
189 | dev_info->is_shared = 1; | ||
190 | switch (dev_info->segment_type) { | ||
191 | case SEG_TYPE_SR: | ||
192 | case SEG_TYPE_ER: | ||
193 | case SEG_TYPE_SC: | ||
194 | set_disk_ro(dev_info->gd,1); | ||
195 | } | 371 | } |
196 | } | 372 | } |
373 | dev_info->is_shared = 1; | ||
374 | switch (dev_info->segment_type) { | ||
375 | case SEG_TYPE_SR: | ||
376 | case SEG_TYPE_ER: | ||
377 | case SEG_TYPE_SC: | ||
378 | set_disk_ro(dev_info->gd, 1); | ||
379 | } | ||
197 | } else if (inbuf[0] == '0') { | 380 | } else if (inbuf[0] == '0') { |
198 | // reload segment in exclusive mode | 381 | /* reload segments in exclusive mode */ |
199 | if (dev_info->segment_type == SEG_TYPE_SC) { | 382 | if (dev_info->segment_type == SEG_TYPE_SC) { |
200 | PRINT_ERR("Segment type SC (%s) cannot be loaded in " | 383 | PRINT_ERR("Segment type SC (%s) cannot be loaded in " |
201 | "non-shared mode\n", dev_info->segment_name); | 384 | "non-shared mode\n", dev_info->segment_name); |
202 | rc = -EINVAL; | 385 | rc = -EINVAL; |
203 | goto out; | 386 | goto out; |
204 | } | 387 | } |
205 | rc = segment_modify_shared(dev_info->segment_name, | 388 | list_for_each_entry(entry, &dev_info->seg_list, lh) { |
206 | SEGMENT_EXCLUSIVE); | 389 | rc = segment_modify_shared(entry->segment_name, |
207 | if (rc < 0) { | 390 | SEGMENT_EXCLUSIVE); |
208 | BUG_ON(rc == -EINVAL); | 391 | if (rc < 0) { |
209 | if (rc != -EAGAIN) | 392 | BUG_ON(rc == -EINVAL); |
210 | goto removeseg; | 393 | if (rc != -EAGAIN) |
211 | } else { | 394 | goto removeseg; |
212 | dev_info->is_shared = 0; | 395 | } |
213 | set_disk_ro(dev_info->gd, 0); | ||
214 | } | 396 | } |
397 | dev_info->is_shared = 0; | ||
398 | set_disk_ro(dev_info->gd, 0); | ||
215 | } else { | 399 | } else { |
216 | rc = -EINVAL; | 400 | rc = -EINVAL; |
217 | goto out; | 401 | goto out; |
@@ -220,8 +404,14 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch | |||
220 | goto out; | 404 | goto out; |
221 | 405 | ||
222 | removeseg: | 406 | removeseg: |
223 | PRINT_ERR("Could not reload segment %s, removing it now!\n", | 407 | PRINT_ERR("Could not reload segment(s) of the device %s, removing " |
224 | dev_info->segment_name); | 408 | "segment(s) now!\n", |
409 | dev_info->segment_name); | ||
410 | temp = entry; | ||
411 | list_for_each_entry(entry, &dev_info->seg_list, lh) { | ||
412 | if (entry != temp) | ||
413 | segment_unload(entry->segment_name); | ||
414 | } | ||
225 | list_del(&dev_info->lh); | 415 | list_del(&dev_info->lh); |
226 | 416 | ||
227 | del_gendisk(dev_info->gd); | 417 | del_gendisk(dev_info->gd); |
@@ -254,6 +444,7 @@ static ssize_t | |||
254 | dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) | 444 | dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) |
255 | { | 445 | { |
256 | struct dcssblk_dev_info *dev_info; | 446 | struct dcssblk_dev_info *dev_info; |
447 | struct segment_info *entry; | ||
257 | 448 | ||
258 | if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) | 449 | if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) |
259 | return -EINVAL; | 450 | return -EINVAL; |
@@ -263,14 +454,16 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char | |||
263 | if (inbuf[0] == '1') { | 454 | if (inbuf[0] == '1') { |
264 | if (atomic_read(&dev_info->use_count) == 0) { | 455 | if (atomic_read(&dev_info->use_count) == 0) { |
265 | // device is idle => we save immediately | 456 | // device is idle => we save immediately |
266 | PRINT_INFO("Saving segment %s\n", | 457 | PRINT_INFO("Saving segment(s) of the device %s\n", |
267 | dev_info->segment_name); | 458 | dev_info->segment_name); |
268 | segment_save(dev_info->segment_name); | 459 | list_for_each_entry(entry, &dev_info->seg_list, lh) { |
460 | segment_save(entry->segment_name); | ||
461 | } | ||
269 | } else { | 462 | } else { |
270 | // device is busy => we save it when it becomes | 463 | // device is busy => we save it when it becomes |
271 | // idle in dcssblk_release | 464 | // idle in dcssblk_release |
272 | PRINT_INFO("Segment %s is currently busy, it will " | 465 | PRINT_INFO("Device %s is currently busy, segment(s) " |
273 | "be saved when it becomes idle...\n", | 466 | "will be saved when it becomes idle...\n", |
274 | dev_info->segment_name); | 467 | dev_info->segment_name); |
275 | dev_info->save_pending = 1; | 468 | dev_info->save_pending = 1; |
276 | } | 469 | } |
@@ -279,7 +472,8 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char | |||
279 | // device is busy & the user wants to undo his save | 472 | // device is busy & the user wants to undo his save |
280 | // request | 473 | // request |
281 | dev_info->save_pending = 0; | 474 | dev_info->save_pending = 0; |
282 | PRINT_INFO("Pending save for segment %s deactivated\n", | 475 | PRINT_INFO("Pending save for segment(s) of the device " |
476 | "%s deactivated\n", | ||
283 | dev_info->segment_name); | 477 | dev_info->segment_name); |
284 | } | 478 | } |
285 | } else { | 479 | } else { |
@@ -291,66 +485,123 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char | |||
291 | } | 485 | } |
292 | 486 | ||
293 | /* | 487 | /* |
488 | * device attribute for showing all segments in a device | ||
489 | */ | ||
490 | static ssize_t | ||
491 | dcssblk_seglist_show(struct device *dev, struct device_attribute *attr, | ||
492 | char *buf) | ||
493 | { | ||
494 | int i; | ||
495 | |||
496 | struct dcssblk_dev_info *dev_info; | ||
497 | struct segment_info *entry; | ||
498 | |||
499 | down_read(&dcssblk_devices_sem); | ||
500 | dev_info = container_of(dev, struct dcssblk_dev_info, dev); | ||
501 | i = 0; | ||
502 | buf[0] = '\0'; | ||
503 | list_for_each_entry(entry, &dev_info->seg_list, lh) { | ||
504 | strcpy(&buf[i], entry->segment_name); | ||
505 | i += strlen(entry->segment_name); | ||
506 | buf[i] = '\n'; | ||
507 | i++; | ||
508 | } | ||
509 | up_read(&dcssblk_devices_sem); | ||
510 | return i; | ||
511 | } | ||
512 | |||
513 | /* | ||
294 | * device attribute for adding devices | 514 | * device attribute for adding devices |
295 | */ | 515 | */ |
296 | static ssize_t | 516 | static ssize_t |
297 | dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | 517 | dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
298 | { | 518 | { |
299 | int rc, i; | 519 | int rc, i, j, num_of_segments; |
300 | struct dcssblk_dev_info *dev_info; | 520 | struct dcssblk_dev_info *dev_info; |
521 | struct segment_info *seg_info, *temp; | ||
301 | char *local_buf; | 522 | char *local_buf; |
302 | unsigned long seg_byte_size; | 523 | unsigned long seg_byte_size; |
303 | 524 | ||
304 | dev_info = NULL; | 525 | dev_info = NULL; |
526 | seg_info = NULL; | ||
305 | if (dev != dcssblk_root_dev) { | 527 | if (dev != dcssblk_root_dev) { |
306 | rc = -EINVAL; | 528 | rc = -EINVAL; |
307 | goto out_nobuf; | 529 | goto out_nobuf; |
308 | } | 530 | } |
531 | if ((count < 1) || (buf[0] == '\0') || (buf[0] == '\n')) { | ||
532 | rc = -ENAMETOOLONG; | ||
533 | goto out_nobuf; | ||
534 | } | ||
535 | |||
309 | local_buf = kmalloc(count + 1, GFP_KERNEL); | 536 | local_buf = kmalloc(count + 1, GFP_KERNEL); |
310 | if (local_buf == NULL) { | 537 | if (local_buf == NULL) { |
311 | rc = -ENOMEM; | 538 | rc = -ENOMEM; |
312 | goto out_nobuf; | 539 | goto out_nobuf; |
313 | } | 540 | } |
541 | |||
314 | /* | 542 | /* |
315 | * parse input | 543 | * parse input |
316 | */ | 544 | */ |
545 | num_of_segments = 0; | ||
317 | for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) { | 546 | for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) { |
318 | local_buf[i] = toupper(buf[i]); | 547 | for (j = i; (buf[j] != ':') && |
548 | (buf[j] != '\0') && | ||
549 | (buf[j] != '\n') && | ||
550 | j < count; j++) { | ||
551 | local_buf[j-i] = toupper(buf[j]); | ||
552 | } | ||
553 | local_buf[j-i] = '\0'; | ||
554 | if (((j - i) == 0) || ((j - i) > 8)) { | ||
555 | rc = -ENAMETOOLONG; | ||
556 | goto seg_list_del; | ||
557 | } | ||
558 | |||
559 | rc = dcssblk_load_segment(local_buf, &seg_info); | ||
560 | if (rc < 0) | ||
561 | goto seg_list_del; | ||
562 | /* | ||
563 | * get a struct dcssblk_dev_info | ||
564 | */ | ||
565 | if (num_of_segments == 0) { | ||
566 | dev_info = kzalloc(sizeof(struct dcssblk_dev_info), | ||
567 | GFP_KERNEL); | ||
568 | if (dev_info == NULL) { | ||
569 | rc = -ENOMEM; | ||
570 | goto out; | ||
571 | } | ||
572 | strcpy(dev_info->segment_name, local_buf); | ||
573 | dev_info->segment_type = seg_info->segment_type; | ||
574 | INIT_LIST_HEAD(&dev_info->seg_list); | ||
575 | } | ||
576 | list_add_tail(&seg_info->lh, &dev_info->seg_list); | ||
577 | num_of_segments++; | ||
578 | i = j; | ||
579 | |||
580 | if ((buf[j] == '\0') || (buf[j] == '\n')) | ||
581 | break; | ||
319 | } | 582 | } |
320 | local_buf[i] = '\0'; | 583 | |
321 | if ((i == 0) || (i > 8)) { | 584 | /* no trailing colon at the end of the input */ |
585 | if ((i > 0) && (buf[i-1] == ':')) { | ||
322 | rc = -ENAMETOOLONG; | 586 | rc = -ENAMETOOLONG; |
323 | goto out; | 587 | goto seg_list_del; |
324 | } | ||
325 | /* | ||
326 | * already loaded? | ||
327 | */ | ||
328 | down_read(&dcssblk_devices_sem); | ||
329 | dev_info = dcssblk_get_device_by_name(local_buf); | ||
330 | up_read(&dcssblk_devices_sem); | ||
331 | if (dev_info != NULL) { | ||
332 | PRINT_WARN("Segment %s already loaded!\n", local_buf); | ||
333 | rc = -EEXIST; | ||
334 | goto out; | ||
335 | } | ||
336 | /* | ||
337 | * get a struct dcssblk_dev_info | ||
338 | */ | ||
339 | dev_info = kzalloc(sizeof(struct dcssblk_dev_info), GFP_KERNEL); | ||
340 | if (dev_info == NULL) { | ||
341 | rc = -ENOMEM; | ||
342 | goto out; | ||
343 | } | 588 | } |
589 | strlcpy(local_buf, buf, i + 1); | ||
590 | dev_info->num_of_segments = num_of_segments; | ||
591 | rc = dcssblk_is_continuous(dev_info); | ||
592 | if (rc < 0) | ||
593 | goto seg_list_del; | ||
594 | |||
595 | dev_info->start = dcssblk_find_lowest_addr(dev_info); | ||
596 | dev_info->end = dcssblk_find_highest_addr(dev_info); | ||
344 | 597 | ||
345 | strcpy(dev_info->segment_name, local_buf); | 598 | dev_set_name(&dev_info->dev, dev_info->segment_name); |
346 | dev_set_name(&dev_info->dev, local_buf); | ||
347 | dev_info->dev.release = dcssblk_release_segment; | 599 | dev_info->dev.release = dcssblk_release_segment; |
348 | INIT_LIST_HEAD(&dev_info->lh); | 600 | INIT_LIST_HEAD(&dev_info->lh); |
349 | |||
350 | dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK); | 601 | dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK); |
351 | if (dev_info->gd == NULL) { | 602 | if (dev_info->gd == NULL) { |
352 | rc = -ENOMEM; | 603 | rc = -ENOMEM; |
353 | goto free_dev_info; | 604 | goto seg_list_del; |
354 | } | 605 | } |
355 | dev_info->gd->major = dcssblk_major; | 606 | dev_info->gd->major = dcssblk_major; |
356 | dev_info->gd->fops = &dcssblk_devops; | 607 | dev_info->gd->fops = &dcssblk_devops; |
@@ -360,59 +611,43 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char | |||
360 | dev_info->gd->driverfs_dev = &dev_info->dev; | 611 | dev_info->gd->driverfs_dev = &dev_info->dev; |
361 | blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request); | 612 | blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request); |
362 | blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096); | 613 | blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096); |
363 | /* | 614 | |
364 | * load the segment | ||
365 | */ | ||
366 | rc = segment_load(local_buf, SEGMENT_SHARED, | ||
367 | &dev_info->start, &dev_info->end); | ||
368 | if (rc < 0) { | ||
369 | segment_warning(rc, dev_info->segment_name); | ||
370 | goto dealloc_gendisk; | ||
371 | } | ||
372 | seg_byte_size = (dev_info->end - dev_info->start + 1); | 615 | seg_byte_size = (dev_info->end - dev_info->start + 1); |
373 | set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors | 616 | set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors |
374 | PRINT_INFO("Loaded segment %s, size = %lu Byte, " | 617 | PRINT_INFO("Loaded segment(s) %s, size = %lu Byte, " |
375 | "capacity = %lu (512 Byte) sectors\n", local_buf, | 618 | "capacity = %lu (512 Byte) sectors\n", local_buf, |
376 | seg_byte_size, seg_byte_size >> 9); | 619 | seg_byte_size, seg_byte_size >> 9); |
377 | 620 | ||
378 | dev_info->segment_type = rc; | ||
379 | dev_info->save_pending = 0; | 621 | dev_info->save_pending = 0; |
380 | dev_info->is_shared = 1; | 622 | dev_info->is_shared = 1; |
381 | dev_info->dev.parent = dcssblk_root_dev; | 623 | dev_info->dev.parent = dcssblk_root_dev; |
382 | 624 | ||
383 | /* | 625 | /* |
384 | * get minor, add to list | 626 | *get minor, add to list |
385 | */ | 627 | */ |
386 | down_write(&dcssblk_devices_sem); | 628 | down_write(&dcssblk_devices_sem); |
387 | if (dcssblk_get_device_by_name(local_buf)) { | 629 | if (dcssblk_get_segment_by_name(local_buf)) { |
388 | up_write(&dcssblk_devices_sem); | ||
389 | rc = -EEXIST; | 630 | rc = -EEXIST; |
390 | goto unload_seg; | 631 | goto release_gd; |
391 | } | 632 | } |
392 | rc = dcssblk_assign_free_minor(dev_info); | 633 | rc = dcssblk_assign_free_minor(dev_info); |
393 | if (rc) { | 634 | if (rc) |
394 | up_write(&dcssblk_devices_sem); | 635 | goto release_gd; |
395 | PRINT_ERR("No free minor number available! " | ||
396 | "Unloading segment...\n"); | ||
397 | goto unload_seg; | ||
398 | } | ||
399 | sprintf(dev_info->gd->disk_name, "dcssblk%d", | 636 | sprintf(dev_info->gd->disk_name, "dcssblk%d", |
400 | MINOR(disk_devt(dev_info->gd))); | 637 | MINOR(disk_devt(dev_info->gd))); |
401 | list_add_tail(&dev_info->lh, &dcssblk_devices); | 638 | list_add_tail(&dev_info->lh, &dcssblk_devices); |
402 | 639 | ||
403 | if (!try_module_get(THIS_MODULE)) { | 640 | if (!try_module_get(THIS_MODULE)) { |
404 | rc = -ENODEV; | 641 | rc = -ENODEV; |
405 | goto list_del; | 642 | goto dev_list_del; |
406 | } | 643 | } |
407 | /* | 644 | /* |
408 | * register the device | 645 | * register the device |
409 | */ | 646 | */ |
410 | rc = device_register(&dev_info->dev); | 647 | rc = device_register(&dev_info->dev); |
411 | if (rc) { | 648 | if (rc) { |
412 | PRINT_ERR("Segment %s could not be registered RC=%d\n", | ||
413 | local_buf, rc); | ||
414 | module_put(THIS_MODULE); | 649 | module_put(THIS_MODULE); |
415 | goto list_del; | 650 | goto dev_list_del; |
416 | } | 651 | } |
417 | get_device(&dev_info->dev); | 652 | get_device(&dev_info->dev); |
418 | rc = device_create_file(&dev_info->dev, &dev_attr_shared); | 653 | rc = device_create_file(&dev_info->dev, &dev_attr_shared); |
@@ -421,6 +656,9 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char | |||
421 | rc = device_create_file(&dev_info->dev, &dev_attr_save); | 656 | rc = device_create_file(&dev_info->dev, &dev_attr_save); |
422 | if (rc) | 657 | if (rc) |
423 | goto unregister_dev; | 658 | goto unregister_dev; |
659 | rc = device_create_file(&dev_info->dev, &dev_attr_seglist); | ||
660 | if (rc) | ||
661 | goto unregister_dev; | ||
424 | 662 | ||
425 | add_disk(dev_info->gd); | 663 | add_disk(dev_info->gd); |
426 | 664 | ||
@@ -434,7 +672,6 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char | |||
434 | set_disk_ro(dev_info->gd,0); | 672 | set_disk_ro(dev_info->gd,0); |
435 | break; | 673 | break; |
436 | } | 674 | } |
437 | PRINT_DEBUG("Segment %s loaded successfully\n", local_buf); | ||
438 | up_write(&dcssblk_devices_sem); | 675 | up_write(&dcssblk_devices_sem); |
439 | rc = count; | 676 | rc = count; |
440 | goto out; | 677 | goto out; |
@@ -445,20 +682,27 @@ unregister_dev: | |||
445 | dev_info->gd->queue = NULL; | 682 | dev_info->gd->queue = NULL; |
446 | put_disk(dev_info->gd); | 683 | put_disk(dev_info->gd); |
447 | device_unregister(&dev_info->dev); | 684 | device_unregister(&dev_info->dev); |
448 | segment_unload(dev_info->segment_name); | 685 | list_for_each_entry(seg_info, &dev_info->seg_list, lh) { |
686 | segment_unload(seg_info->segment_name); | ||
687 | } | ||
449 | put_device(&dev_info->dev); | 688 | put_device(&dev_info->dev); |
450 | up_write(&dcssblk_devices_sem); | 689 | up_write(&dcssblk_devices_sem); |
451 | goto out; | 690 | goto out; |
452 | list_del: | 691 | dev_list_del: |
453 | list_del(&dev_info->lh); | 692 | list_del(&dev_info->lh); |
454 | up_write(&dcssblk_devices_sem); | 693 | release_gd: |
455 | unload_seg: | ||
456 | segment_unload(local_buf); | ||
457 | dealloc_gendisk: | ||
458 | blk_cleanup_queue(dev_info->dcssblk_queue); | 694 | blk_cleanup_queue(dev_info->dcssblk_queue); |
459 | dev_info->gd->queue = NULL; | 695 | dev_info->gd->queue = NULL; |
460 | put_disk(dev_info->gd); | 696 | put_disk(dev_info->gd); |
461 | free_dev_info: | 697 | up_write(&dcssblk_devices_sem); |
698 | seg_list_del: | ||
699 | if (dev_info == NULL) | ||
700 | goto out; | ||
701 | list_for_each_entry_safe(seg_info, temp, &dev_info->seg_list, lh) { | ||
702 | list_del(&seg_info->lh); | ||
703 | segment_unload(seg_info->segment_name); | ||
704 | kfree(seg_info); | ||
705 | } | ||
462 | kfree(dev_info); | 706 | kfree(dev_info); |
463 | out: | 707 | out: |
464 | kfree(local_buf); | 708 | kfree(local_buf); |
@@ -473,6 +717,7 @@ static ssize_t | |||
473 | dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | 717 | dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
474 | { | 718 | { |
475 | struct dcssblk_dev_info *dev_info; | 719 | struct dcssblk_dev_info *dev_info; |
720 | struct segment_info *entry; | ||
476 | int rc, i; | 721 | int rc, i; |
477 | char *local_buf; | 722 | char *local_buf; |
478 | 723 | ||
@@ -499,26 +744,28 @@ dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const ch | |||
499 | dev_info = dcssblk_get_device_by_name(local_buf); | 744 | dev_info = dcssblk_get_device_by_name(local_buf); |
500 | if (dev_info == NULL) { | 745 | if (dev_info == NULL) { |
501 | up_write(&dcssblk_devices_sem); | 746 | up_write(&dcssblk_devices_sem); |
502 | PRINT_WARN("Segment %s is not loaded!\n", local_buf); | 747 | PRINT_WARN("Device %s is not loaded!\n", local_buf); |
503 | rc = -ENODEV; | 748 | rc = -ENODEV; |
504 | goto out_buf; | 749 | goto out_buf; |
505 | } | 750 | } |
506 | if (atomic_read(&dev_info->use_count) != 0) { | 751 | if (atomic_read(&dev_info->use_count) != 0) { |
507 | up_write(&dcssblk_devices_sem); | 752 | up_write(&dcssblk_devices_sem); |
508 | PRINT_WARN("Segment %s is in use!\n", local_buf); | 753 | PRINT_WARN("Device %s is in use!\n", local_buf); |
509 | rc = -EBUSY; | 754 | rc = -EBUSY; |
510 | goto out_buf; | 755 | goto out_buf; |
511 | } | 756 | } |
512 | list_del(&dev_info->lh); | ||
513 | 757 | ||
758 | list_del(&dev_info->lh); | ||
514 | del_gendisk(dev_info->gd); | 759 | del_gendisk(dev_info->gd); |
515 | blk_cleanup_queue(dev_info->dcssblk_queue); | 760 | blk_cleanup_queue(dev_info->dcssblk_queue); |
516 | dev_info->gd->queue = NULL; | 761 | dev_info->gd->queue = NULL; |
517 | put_disk(dev_info->gd); | 762 | put_disk(dev_info->gd); |
518 | device_unregister(&dev_info->dev); | 763 | device_unregister(&dev_info->dev); |
519 | segment_unload(dev_info->segment_name); | 764 | |
520 | PRINT_DEBUG("Segment %s unloaded successfully\n", | 765 | /* unload all related segments */ |
521 | dev_info->segment_name); | 766 | list_for_each_entry(entry, &dev_info->seg_list, lh) |
767 | segment_unload(entry->segment_name); | ||
768 | |||
522 | put_device(&dev_info->dev); | 769 | put_device(&dev_info->dev); |
523 | up_write(&dcssblk_devices_sem); | 770 | up_write(&dcssblk_devices_sem); |
524 | 771 | ||
@@ -550,6 +797,7 @@ static int | |||
550 | dcssblk_release(struct inode *inode, struct file *filp) | 797 | dcssblk_release(struct inode *inode, struct file *filp) |
551 | { | 798 | { |
552 | struct dcssblk_dev_info *dev_info; | 799 | struct dcssblk_dev_info *dev_info; |
800 | struct segment_info *entry; | ||
553 | int rc; | 801 | int rc; |
554 | 802 | ||
555 | dev_info = inode->i_bdev->bd_disk->private_data; | 803 | dev_info = inode->i_bdev->bd_disk->private_data; |
@@ -560,9 +808,11 @@ dcssblk_release(struct inode *inode, struct file *filp) | |||
560 | down_write(&dcssblk_devices_sem); | 808 | down_write(&dcssblk_devices_sem); |
561 | if (atomic_dec_and_test(&dev_info->use_count) | 809 | if (atomic_dec_and_test(&dev_info->use_count) |
562 | && (dev_info->save_pending)) { | 810 | && (dev_info->save_pending)) { |
563 | PRINT_INFO("Segment %s became idle and is being saved now\n", | 811 | PRINT_INFO("Device %s became idle and is being saved now\n", |
564 | dev_info->segment_name); | 812 | dev_info->segment_name); |
565 | segment_save(dev_info->segment_name); | 813 | list_for_each_entry(entry, &dev_info->seg_list, lh) { |
814 | segment_save(entry->segment_name); | ||
815 | } | ||
566 | dev_info->save_pending = 0; | 816 | dev_info->save_pending = 0; |
567 | } | 817 | } |
568 | up_write(&dcssblk_devices_sem); | 818 | up_write(&dcssblk_devices_sem); |
@@ -602,7 +852,7 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio) | |||
602 | case SEG_TYPE_SC: | 852 | case SEG_TYPE_SC: |
603 | /* cannot write to these segments */ | 853 | /* cannot write to these segments */ |
604 | if (bio_data_dir(bio) == WRITE) { | 854 | if (bio_data_dir(bio) == WRITE) { |
605 | PRINT_WARN("rejecting write to ro segment %s\n", | 855 | PRINT_WARN("rejecting write to ro device %s\n", |
606 | dev_name(&dev_info->dev)); | 856 | dev_name(&dev_info->dev)); |
607 | goto fail; | 857 | goto fail; |
608 | } | 858 | } |
@@ -658,7 +908,7 @@ static void | |||
658 | dcssblk_check_params(void) | 908 | dcssblk_check_params(void) |
659 | { | 909 | { |
660 | int rc, i, j, k; | 910 | int rc, i, j, k; |
661 | char buf[9]; | 911 | char buf[DCSSBLK_PARM_LEN + 1]; |
662 | struct dcssblk_dev_info *dev_info; | 912 | struct dcssblk_dev_info *dev_info; |
663 | 913 | ||
664 | for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0'); | 914 | for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0'); |
@@ -666,15 +916,16 @@ dcssblk_check_params(void) | |||
666 | for (j = i; (dcssblk_segments[j] != ',') && | 916 | for (j = i; (dcssblk_segments[j] != ',') && |
667 | (dcssblk_segments[j] != '\0') && | 917 | (dcssblk_segments[j] != '\0') && |
668 | (dcssblk_segments[j] != '(') && | 918 | (dcssblk_segments[j] != '(') && |
669 | (j - i) < 8; j++) | 919 | (j < DCSSBLK_PARM_LEN); j++) |
670 | { | 920 | { |
671 | buf[j-i] = dcssblk_segments[j]; | 921 | buf[j-i] = dcssblk_segments[j]; |
672 | } | 922 | } |
673 | buf[j-i] = '\0'; | 923 | buf[j-i] = '\0'; |
674 | rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i); | 924 | rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i); |
675 | if ((rc >= 0) && (dcssblk_segments[j] == '(')) { | 925 | if ((rc >= 0) && (dcssblk_segments[j] == '(')) { |
676 | for (k = 0; buf[k] != '\0'; k++) | 926 | for (k = 0; (buf[k] != ':') && (buf[k] != '\0'); k++) |
677 | buf[k] = toupper(buf[k]); | 927 | buf[k] = toupper(buf[k]); |
928 | buf[k] = '\0'; | ||
678 | if (!strncmp(&dcssblk_segments[j], "(local)", 7)) { | 929 | if (!strncmp(&dcssblk_segments[j], "(local)", 7)) { |
679 | down_read(&dcssblk_devices_sem); | 930 | down_read(&dcssblk_devices_sem); |
680 | dev_info = dcssblk_get_device_by_name(buf); | 931 | dev_info = dcssblk_get_device_by_name(buf); |
@@ -741,10 +992,12 @@ module_exit(dcssblk_exit); | |||
741 | 992 | ||
742 | module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444); | 993 | module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444); |
743 | MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, " | 994 | MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, " |
744 | "comma-separated list, each name max. 8 chars.\n" | 995 | "comma-separated list, names in each set separated " |
745 | "Adding \"(local)\" to segment name equals echoing 0 to " | 996 | "by commas are separated by colons, each set contains " |
746 | "/sys/devices/dcssblk/<segment name>/shared after loading " | 997 | "names of contiguous segments and each name max. 8 chars.\n" |
747 | "the segment - \n" | 998 | "Adding \"(local)\" to the end of each set equals echoing 0 " |
748 | "e.g. segments=\"mydcss1,mydcss2,mydcss3(local)\""); | 999 | "to /sys/devices/dcssblk/<device name>/shared after loading " |
1000 | "the contiguous segments - \n" | ||
1001 | "e.g. segments=\"mydcss1,mydcss2:mydcss3,mydcss4(local)\""); | ||
749 | 1002 | ||
750 | MODULE_LICENSE("GPL"); | 1003 | MODULE_LICENSE("GPL"); |