]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blame - src/patches/suse-2.6.27.25/patches.arch/s390-01-02-dcss-64-v2.patch
Revert "Move xen patchset to new version's subdir."
[people/pmueller/ipfire-2.x.git] / src / patches / suse-2.6.27.25 / patches.arch / s390-01-02-dcss-64-v2.patch
CommitLineData
00e5a55c
BS
1From: Gerald Schaefer <geraldsc@de.ibm.com>
2Subject: dcssblk (new function): Add support for >2G DCSS and stacked
3 contiguous DCSS support.
4References: bnc#417246
5
6Description: The DCSS block device driver is modified to add >2G DCSS support
7 and allow a DCSS block device to map to a set of contiguous DCSSs.
8 The extmem code is also modified to use new Diagnose x'64' subcodes
9 for >2G DCSSs.
10
11Acked-by: John Jolly <jjolly@suse.de>
12
13---
14
15 arch/s390/mm/extmem.c | 251 +++++++++++++++++---
16 drivers/s390/block/dcssblk.c | 516 ++++++++++++++++++++++++++++++++-----------
17 2 files changed, 597 insertions(+), 170 deletions(-)
18
19Index: linux-sles11/arch/s390/mm/extmem.c
20===================================================================
21--- linux-sles11.orig/arch/s390/mm/extmem.c
22+++ linux-sles11/arch/s390/mm/extmem.c
23@@ -43,20 +43,40 @@
24 #define DCSS_FINDSEG 0x0c
25 #define DCSS_LOADNOLY 0x10
26 #define DCSS_SEGEXT 0x18
27+#define DCSS_LOADSHRX 0x20
28+#define DCSS_LOADNSRX 0x24
29+#define DCSS_FINDSEGX 0x2c
30+#define DCSS_SEGEXTX 0x38
31 #define DCSS_FINDSEGA 0x0c
32
33 struct qrange {
34- unsigned int start; // 3byte start address, 1 byte type
35- unsigned int end; // 3byte end address, 1 byte reserved
36+ unsigned long start; /* last byte type */
37+ unsigned long end; /* last byte reserved */
38 };
39
40 struct qout64 {
41+ unsigned long segstart;
42+ unsigned long segend;
43+ int segcnt;
44+ int segrcnt;
45+ struct qrange range[6];
46+};
47+
48+#ifdef CONFIG_64BIT
49+struct qrange_old {
50+ unsigned int start; /* last byte type */
51+ unsigned int end; /* last byte reserved */
52+};
53+
54+/* output area format for the Diag x'64' old subcode x'18' */
55+struct qout64_old {
56 int segstart;
57 int segend;
58 int segcnt;
59 int segrcnt;
60- struct qrange range[6];
61+ struct qrange_old range[6];
62 };
63+#endif
64
65 struct qin64 {
66 char qopcode;
67@@ -86,6 +106,55 @@ static DEFINE_MUTEX(dcss_lock);
68 static LIST_HEAD(dcss_list);
69 static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
70 "EW/EN-MIXED" };
71+static int loadshr_scode, loadnsr_scode, findseg_scode;
72+static int segext_scode, purgeseg_scode;
73+static int scode_set;
74+
75+/* set correct Diag x'64' subcodes. */
76+static int
77+dcss_set_subcodes(void)
78+{
79+#ifdef CONFIG_64BIT
80+ char *name = kmalloc(8 * sizeof(char), GFP_DMA);
81+ unsigned long rx, ry;
82+ int rc;
83+
84+ if (name == NULL)
85+ return -ENOMEM;
86+
87+ rx = (unsigned long) name;
88+ ry = DCSS_FINDSEGX;
89+
90+ strcpy(name, "dummy");
91+ asm volatile(
92+ " diag %0,%1,0x64\n"
93+ "0: ipm %2\n"
94+ " srl %2,28\n"
95+ " j 2f\n"
96+ "1: la %2,3\n"
97+ "2:\n"
98+ EX_TABLE(0b, 1b)
99+ : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
100+
101+ kfree(name);
102+ /* Diag x'64' new subcodes are supported, set to new subcodes */
103+ if (rc != 3) {
104+ loadshr_scode = DCSS_LOADSHRX;
105+ loadnsr_scode = DCSS_LOADNSRX;
106+ purgeseg_scode = DCSS_PURGESEG;
107+ findseg_scode = DCSS_FINDSEGX;
108+ segext_scode = DCSS_SEGEXTX;
109+ return 0;
110+ }
111+#endif
112+ /* Diag x'64' new subcodes are not supported, set to old subcodes */
113+ loadshr_scode = DCSS_LOADNOLY;
114+ loadnsr_scode = DCSS_LOADNSR;
115+ purgeseg_scode = DCSS_PURGESEG;
116+ findseg_scode = DCSS_FINDSEG;
117+ segext_scode = DCSS_SEGEXT;
118+ return 0;
119+}
120
121 /*
122 * Create the 8 bytes, ebcdic VM segment name from
123@@ -135,25 +204,45 @@ segment_by_name (char *name)
124 * Perform a function on a dcss segment.
125 */
126 static inline int
127-dcss_diag (__u8 func, void *parameter,
128+dcss_diag(int *func, void *parameter,
129 unsigned long *ret1, unsigned long *ret2)
130 {
131 unsigned long rx, ry;
132 int rc;
133
134+ if (scode_set == 0) {
135+ rc = dcss_set_subcodes();
136+ if (rc < 0)
137+ return rc;
138+ scode_set = 1;
139+ }
140 rx = (unsigned long) parameter;
141- ry = (unsigned long) func;
142- asm volatile(
143+ ry = (unsigned long) *func;
144+
145 #ifdef CONFIG_64BIT
146- " sam31\n"
147- " diag %0,%1,0x64\n"
148- " sam64\n"
149+ /* 64-bit Diag x'64' new subcode, keep in 64-bit addressing mode */
150+ if (*func > DCSS_SEGEXT)
151+ asm volatile(
152+ " diag %0,%1,0x64\n"
153+ " ipm %2\n"
154+ " srl %2,28\n"
155+ : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
156+ /* 31-bit Diag x'64' old subcode, switch to 31-bit addressing mode */
157+ else
158+ asm volatile(
159+ " sam31\n"
160+ " diag %0,%1,0x64\n"
161+ " sam64\n"
162+ " ipm %2\n"
163+ " srl %2,28\n"
164+ : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
165 #else
166+ asm volatile(
167 " diag %0,%1,0x64\n"
168-#endif
169 " ipm %2\n"
170 " srl %2,28\n"
171 : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
172+#endif
173 *ret1 = rx;
174 *ret2 = ry;
175 return rc;
176@@ -190,14 +279,45 @@ query_segment_type (struct dcss_segment
177 qin->qoutlen = sizeof(struct qout64);
178 memcpy (qin->qname, seg->dcss_name, 8);
179
180- diag_cc = dcss_diag (DCSS_SEGEXT, qin, &dummy, &vmrc);
181+ diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc);
182
183+ if (diag_cc < 0) {
184+ rc = diag_cc;
185+ goto out_free;
186+ }
187 if (diag_cc > 1) {
188 PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc);
189 rc = dcss_diag_translate_rc (vmrc);
190 goto out_free;
191 }
192
193+#ifdef CONFIG_64BIT
194+ /* Only old format of output area of Diagnose x'64' is supported,
195+ copy data for the new format. */
196+ if (segext_scode == DCSS_SEGEXT) {
197+ struct qout64_old *qout_old;
198+ qout_old = kzalloc(sizeof(struct qout64_old), GFP_DMA);
199+ if (qout_old == NULL) {
200+ rc = -ENOMEM;
201+ goto out_free;
202+ }
203+ memcpy(qout_old, qout, sizeof(struct qout64_old));
204+ qout->segstart = (unsigned long) qout_old->segstart;
205+ qout->segend = (unsigned long) qout_old->segend;
206+ qout->segcnt = qout_old->segcnt;
207+ qout->segrcnt = qout_old->segrcnt;
208+
209+ if (qout->segcnt > 6)
210+ qout->segrcnt = 6;
211+ for (i = 0; i < qout->segrcnt; i++) {
212+ qout->range[i].start =
213+ (unsigned long) qout_old->range[i].start;
214+ qout->range[i].end =
215+ (unsigned long) qout_old->range[i].end;
216+ }
217+ kfree(qout_old);
218+ }
219+#endif
220 if (qout->segcnt > 6) {
221 rc = -ENOTSUPP;
222 goto out_free;
223@@ -269,6 +389,30 @@ segment_type (char* name)
224 }
225
226 /*
227+ * check if segment collides with other segments that are currently loaded
228+ * returns 1 if this is the case, 0 if no collision was found
229+ */
230+static int
231+segment_overlaps_others (struct dcss_segment *seg)
232+{
233+ struct list_head *l;
234+ struct dcss_segment *tmp;
235+
236+ BUG_ON(!mutex_is_locked(&dcss_lock));
237+ list_for_each(l, &dcss_list) {
238+ tmp = list_entry(l, struct dcss_segment, list);
239+ if ((tmp->start_addr >> 20) > (seg->end >> 20))
240+ continue;
241+ if ((tmp->end >> 20) < (seg->start_addr >> 20))
242+ continue;
243+ if (seg == tmp)
244+ continue;
245+ return 1;
246+ }
247+ return 0;
248+}
249+
250+/*
251 * real segment loading function, called from segment_load
252 */
253 static int
254@@ -276,7 +420,8 @@ __segment_load (char *name, int do_nonsh
255 {
256 struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment),
257 GFP_DMA);
258- int dcss_command, rc, diag_cc;
259+ int rc, diag_cc;
260+ unsigned long start_addr, end_addr, dummy;
261
262 if (seg == NULL) {
263 rc = -ENOMEM;
264@@ -287,6 +432,13 @@ __segment_load (char *name, int do_nonsh
265 if (rc < 0)
266 goto out_free;
267
268+ if (loadshr_scode == DCSS_LOADSHRX) {
269+ if (segment_overlaps_others(seg)) {
270+ rc = -EBUSY;
271+ goto out_free;
272+ }
273+ }
274+
275 rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
276
277 if (rc)
278@@ -316,20 +468,28 @@ __segment_load (char *name, int do_nonsh
279 }
280
281 if (do_nonshared)
282- dcss_command = DCSS_LOADNSR;
283+ diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
284+ &start_addr, &end_addr);
285 else
286- dcss_command = DCSS_LOADNOLY;
287-
288- diag_cc = dcss_diag(dcss_command, seg->dcss_name,
289- &seg->start_addr, &seg->end);
290+ diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
291+ &start_addr, &end_addr);
292+ if (diag_cc < 0) {
293+ dcss_diag(&purgeseg_scode, seg->dcss_name,
294+ &dummy, &dummy);
295+ rc = diag_cc;
296+ goto out_resource;
297+ }
298 if (diag_cc > 1) {
299 PRINT_WARN ("segment_load: could not load segment %s - "
300- "diag returned error (%ld)\n",name,seg->end);
301- rc = dcss_diag_translate_rc (seg->end);
302- dcss_diag(DCSS_PURGESEG, seg->dcss_name,
303- &seg->start_addr, &seg->end);
304+ "diag returned error (%ld)\n",
305+ name, end_addr);
306+ rc = dcss_diag_translate_rc(end_addr);
307+ dcss_diag(&purgeseg_scode, seg->dcss_name,
308+ &dummy, &dummy);
309 goto out_resource;
310 }
311+ seg->start_addr = start_addr;
312+ seg->end = end_addr;
313 seg->do_nonshared = do_nonshared;
314 atomic_set(&seg->ref_count, 1);
315 list_add(&seg->list, &dcss_list);
316@@ -423,8 +583,8 @@ int
317 segment_modify_shared (char *name, int do_nonshared)
318 {
319 struct dcss_segment *seg;
320- unsigned long dummy;
321- int dcss_command, rc, diag_cc;
322+ unsigned long start_addr, end_addr, dummy;
323+ int rc, diag_cc;
324
325 mutex_lock(&dcss_lock);
326 seg = segment_by_name (name);
327@@ -445,38 +605,51 @@ segment_modify_shared (char *name, int d
328 goto out_unlock;
329 }
330 release_resource(seg->res);
331- if (do_nonshared) {
332- dcss_command = DCSS_LOADNSR;
333+ if (do_nonshared)
334 seg->res->flags &= ~IORESOURCE_READONLY;
335- } else {
336- dcss_command = DCSS_LOADNOLY;
337+ else
338 if (seg->vm_segtype == SEG_TYPE_SR ||
339 seg->vm_segtype == SEG_TYPE_ER)
340 seg->res->flags |= IORESOURCE_READONLY;
341- }
342+
343 if (request_resource(&iomem_resource, seg->res)) {
344 PRINT_WARN("segment_modify_shared: could not reload segment %s"
345 " - overlapping resources\n", name);
346 rc = -EBUSY;
347 kfree(seg->res);
348- goto out_del;
349+ goto out_del_mem;
350+ }
351+
352+ dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
353+ if (do_nonshared)
354+ diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
355+ &start_addr, &end_addr);
356+ else
357+ diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
358+ &start_addr, &end_addr);
359+ if (diag_cc < 0) {
360+ rc = diag_cc;
361+ goto out_del_res;
362 }
363- dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
364- diag_cc = dcss_diag(dcss_command, seg->dcss_name,
365- &seg->start_addr, &seg->end);
366 if (diag_cc > 1) {
367 PRINT_WARN ("segment_modify_shared: could not reload segment %s"
368- " - diag returned error (%ld)\n",name,seg->end);
369- rc = dcss_diag_translate_rc (seg->end);
370- goto out_del;
371+ " - diag returned error (%ld)\n",
372+ name, end_addr);
373+ rc = dcss_diag_translate_rc(end_addr);
374+ goto out_del_res;
375 }
376+ seg->start_addr = start_addr;
377+ seg->end = end_addr;
378 seg->do_nonshared = do_nonshared;
379 rc = 0;
380 goto out_unlock;
381- out_del:
382+ out_del_res:
383+ release_resource(seg->res);
384+ kfree(seg->res);
385+ out_del_mem:
386 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
387 list_del(&seg->list);
388- dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
389+ dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
390 kfree(seg);
391 out_unlock:
392 mutex_unlock(&dcss_lock);
393@@ -510,7 +683,7 @@ segment_unload(char *name)
394 kfree(seg->res);
395 vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
396 list_del(&seg->list);
397- dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
398+ dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
399 kfree(seg);
400 out_unlock:
401 mutex_unlock(&dcss_lock);
402@@ -545,7 +718,7 @@ segment_save(char *name)
403 endpfn = (seg->end) >> PAGE_SHIFT;
404 sprintf(cmd1, "DEFSEG %s", name);
405 for (i=0; i<seg->segcnt; i++) {
406- sprintf(cmd1+strlen(cmd1), " %X-%X %s",
407+ sprintf(cmd1+strlen(cmd1), " %lX-%lX %s",
408 seg->range[i].start >> PAGE_SHIFT,
409 seg->range[i].end >> PAGE_SHIFT,
410 segtype_string[seg->range[i].start & 0xff]);
411Index: linux-sles11/drivers/s390/block/dcssblk.c
412===================================================================
413--- linux-sles11.orig/drivers/s390/block/dcssblk.c
414+++ linux-sles11/drivers/s390/block/dcssblk.c
415@@ -31,7 +31,6 @@
416 #define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x)
417 #define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x)
418
419-
420 static int dcssblk_open(struct inode *inode, struct file *filp);
421 static int dcssblk_release(struct inode *inode, struct file *filp);
422 static int dcssblk_make_request(struct request_queue *q, struct bio *bio);
423@@ -48,6 +47,30 @@ static struct block_device_operations dc
424 .direct_access = dcssblk_direct_access,
425 };
426
427+struct dcssblk_dev_info {
428+ struct list_head lh;
429+ struct device dev;
430+ char segment_name[BUS_ID_SIZE];
431+ atomic_t use_count;
432+ struct gendisk *gd;
433+ unsigned long start;
434+ unsigned long end;
435+ int segment_type;
436+ unsigned char save_pending;
437+ unsigned char is_shared;
438+ struct request_queue *dcssblk_queue;
439+ int num_of_segments;
440+ struct list_head seg_list;
441+};
442+
443+struct segment_info {
444+ struct list_head lh;
445+ char segment_name[BUS_ID_SIZE];
446+ unsigned long start;
447+ unsigned long end;
448+ int segment_type;
449+};
450+
451 static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf,
452 size_t count);
453 static ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf,
454@@ -58,30 +81,20 @@ static ssize_t dcssblk_save_show(struct
455 static ssize_t dcssblk_shared_store(struct device * dev, struct device_attribute *attr, const char * buf,
456 size_t count);
457 static ssize_t dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf);
458+static ssize_t dcssblk_seglist_show(struct device *dev,
459+ struct device_attribute *attr,
460+ char *buf);
461
462 static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store);
463 static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store);
464-static DEVICE_ATTR(save, S_IWUSR | S_IRUGO, dcssblk_save_show,
465+static DEVICE_ATTR(save, S_IWUSR | S_IRUSR, dcssblk_save_show,
466 dcssblk_save_store);
467-static DEVICE_ATTR(shared, S_IWUSR | S_IRUGO, dcssblk_shared_show,
468+static DEVICE_ATTR(shared, S_IWUSR | S_IRUSR, dcssblk_shared_show,
469 dcssblk_shared_store);
470+static DEVICE_ATTR(seglist, S_IRUSR, dcssblk_seglist_show, NULL);
471
472 static struct device *dcssblk_root_dev;
473
474-struct dcssblk_dev_info {
475- struct list_head lh;
476- struct device dev;
477- char segment_name[BUS_ID_SIZE];
478- atomic_t use_count;
479- struct gendisk *gd;
480- unsigned long start;
481- unsigned long end;
482- int segment_type;
483- unsigned char save_pending;
484- unsigned char is_shared;
485- struct request_queue *dcssblk_queue;
486-};
487-
488 static LIST_HEAD(dcssblk_devices);
489 static struct rw_semaphore dcssblk_devices_sem;
490
491@@ -91,8 +104,15 @@ static struct rw_semaphore dcssblk_devic
492 static void
493 dcssblk_release_segment(struct device *dev)
494 {
495- PRINT_DEBUG("segment release fn called for %s\n", dev->bus_id);
496- kfree(container_of(dev, struct dcssblk_dev_info, dev));
497+ struct dcssblk_dev_info *dev_info;
498+ struct segment_info *entry, *temp;
499+
500+ dev_info = container_of(dev, struct dcssblk_dev_info, dev);
501+ list_for_each_entry_safe(entry, temp, &dev_info->seg_list, lh) {
502+ list_del(&entry->lh);
503+ kfree(entry);
504+ }
505+ kfree(dev_info);
506 module_put(THIS_MODULE);
507 }
508
509@@ -142,6 +162,169 @@ dcssblk_get_device_by_name(char *name)
510 return NULL;
511 }
512
513+/*
514+ * get the struct segment_info from seg_list
515+ * for the given name.
516+ * down_read(&dcssblk_devices_sem) must be held.
517+ */
518+static struct segment_info *
519+dcssblk_get_segment_by_name(char *name)
520+{
521+ struct dcssblk_dev_info *dev_info;
522+ struct segment_info *entry;
523+
524+ list_for_each_entry(dev_info, &dcssblk_devices, lh) {
525+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
526+ if (!strcmp(name, entry->segment_name))
527+ return entry;
528+ }
529+ }
530+ return NULL;
531+}
532+
533+/*
534+ * get the highest address of the multi-segment block.
535+ */
536+static unsigned long
537+dcssblk_find_highest_addr(struct dcssblk_dev_info *dev_info)
538+{
539+ unsigned long highest_addr;
540+ struct segment_info *entry;
541+
542+ highest_addr = 0;
543+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
544+ if (highest_addr < entry->end)
545+ highest_addr = entry->end;
546+ }
547+ return highest_addr;
548+}
549+
550+/*
551+ * get the lowest address of the multi-segment block.
552+ */
553+static unsigned long
554+dcssblk_find_lowest_addr(struct dcssblk_dev_info *dev_info)
555+{
556+ int set_first;
557+ unsigned long lowest_addr;
558+ struct segment_info *entry;
559+
560+ set_first = 0;
561+ lowest_addr = 0;
562+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
563+ if (set_first == 0) {
564+ lowest_addr = entry->start;
565+ set_first = 1;
566+ } else {
567+ if (lowest_addr > entry->start)
568+ lowest_addr = entry->start;
569+ }
570+ }
571+ return lowest_addr;
572+}
573+
574+/*
575+ * Check continuity of segments.
576+ */
577+static int
578+dcssblk_is_continuous(struct dcssblk_dev_info *dev_info)
579+{
580+ int i, j, rc;
581+ struct segment_info *sort_list, *entry, temp;
582+
583+ if (dev_info->num_of_segments <= 1)
584+ return 0;
585+
586+ sort_list = kzalloc(
587+ sizeof(struct segment_info) * dev_info->num_of_segments,
588+ GFP_KERNEL);
589+ if (sort_list == NULL)
590+ return -ENOMEM;
591+ i = 0;
592+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
593+ memcpy(&sort_list[i], entry, sizeof(struct segment_info));
594+ i++;
595+ }
596+
597+ /* sort segments */
598+ for (i = 0; i < dev_info->num_of_segments; i++)
599+ for (j = 0; j < dev_info->num_of_segments; j++)
600+ if (sort_list[j].start > sort_list[i].start) {
601+ memcpy(&temp, &sort_list[i],
602+ sizeof(struct segment_info));
603+ memcpy(&sort_list[i], &sort_list[j],
604+ sizeof(struct segment_info));
605+ memcpy(&sort_list[j], &temp,
606+ sizeof(struct segment_info));
607+ }
608+
609+ /* check continuity */
610+ for (i = 0; i < dev_info->num_of_segments - 1; i++) {
611+ if ((sort_list[i].end + 1) != sort_list[i+1].start) {
612+ PRINT_ERR("Segment %s is not contiguous with "
613+ "segment %s\n",
614+ sort_list[i].segment_name,
615+ sort_list[i+1].segment_name);
616+ rc = -EINVAL;
617+ goto out;
618+ }
619+ /* EN and EW are allowed in a block device */
620+ if (sort_list[i].segment_type != sort_list[i+1].segment_type) {
621+ if (!(sort_list[i].segment_type & SEGMENT_EXCLUSIVE) ||
622+ (sort_list[i].segment_type == SEG_TYPE_ER) ||
623+ !(sort_list[i+1].segment_type &
624+ SEGMENT_EXCLUSIVE) ||
625+ (sort_list[i+1].segment_type == SEG_TYPE_ER)) {
626+ PRINT_ERR("Segment %s has different type from "
627+ "segment %s\n",
628+ sort_list[i].segment_name,
629+ sort_list[i+1].segment_name);
630+ rc = -EINVAL;
631+ goto out;
632+ }
633+ }
634+ }
635+ rc = 0;
636+out:
637+ kfree(sort_list);
638+ return rc;
639+}
640+
641+/*
642+ * Load a segment
643+ */
644+static int
645+dcssblk_load_segment(char *name, struct segment_info **seg_info)
646+{
647+ int rc;
648+
649+ /* already loaded? */
650+ down_read(&dcssblk_devices_sem);
651+ *seg_info = dcssblk_get_segment_by_name(name);
652+ up_read(&dcssblk_devices_sem);
653+ if (*seg_info != NULL)
654+ return -EEXIST;
655+
656+ /* get a struct segment_info */
657+ *seg_info = kzalloc(sizeof(struct segment_info), GFP_KERNEL);
658+ if (*seg_info == NULL)
659+ return -ENOMEM;
660+
661+ strcpy((*seg_info)->segment_name, name);
662+
663+ /* load the segment */
664+ rc = segment_load(name, SEGMENT_SHARED,
665+ &(*seg_info)->start, &(*seg_info)->end);
666+ if (rc < 0) {
667+ segment_warning(rc, (*seg_info)->segment_name);
668+ kfree(*seg_info);
669+ } else {
670+ INIT_LIST_HEAD(&(*seg_info)->lh);
671+ (*seg_info)->segment_type = rc;
672+ }
673+ return rc;
674+}
675+
676 static void dcssblk_unregister_callback(struct device *dev)
677 {
678 device_unregister(dev);
679@@ -165,6 +348,7 @@ static ssize_t
680 dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
681 {
682 struct dcssblk_dev_info *dev_info;
683+ struct segment_info *entry, *temp;
684 int rc;
685
686 if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
687@@ -172,46 +356,46 @@ dcssblk_shared_store(struct device *dev,
688 down_write(&dcssblk_devices_sem);
689 dev_info = container_of(dev, struct dcssblk_dev_info, dev);
690 if (atomic_read(&dev_info->use_count)) {
691- PRINT_ERR("share: segment %s is busy!\n",
692- dev_info->segment_name);
693 rc = -EBUSY;
694 goto out;
695 }
696 if (inbuf[0] == '1') {
697- // reload segment in shared mode
698- rc = segment_modify_shared(dev_info->segment_name,
699- SEGMENT_SHARED);
700- if (rc < 0) {
701- BUG_ON(rc == -EINVAL);
702- if (rc != -EAGAIN)
703- goto removeseg;
704- } else {
705- dev_info->is_shared = 1;
706- switch (dev_info->segment_type) {
707- case SEG_TYPE_SR:
708- case SEG_TYPE_ER:
709- case SEG_TYPE_SC:
710- set_disk_ro(dev_info->gd,1);
711+ /* reload segments in shared mode */
712+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
713+ rc = segment_modify_shared(entry->segment_name,
714+ SEGMENT_SHARED);
715+ if (rc < 0) {
716+ BUG_ON(rc == -EINVAL);
717+ if (rc != -EAGAIN)
718+ goto removeseg;
719 }
720 }
721+ dev_info->is_shared = 1;
722+ switch (dev_info->segment_type) {
723+ case SEG_TYPE_SR:
724+ case SEG_TYPE_ER:
725+ case SEG_TYPE_SC:
726+ set_disk_ro(dev_info->gd, 1);
727+ }
728 } else if (inbuf[0] == '0') {
729- // reload segment in exclusive mode
730+ /* reload segments in exclusive mode */
731 if (dev_info->segment_type == SEG_TYPE_SC) {
732 PRINT_ERR("Segment type SC (%s) cannot be loaded in "
733- "non-shared mode\n", dev_info->segment_name);
734+ "non-shared mode\n", dev_info->segment_name);
735 rc = -EINVAL;
736 goto out;
737 }
738- rc = segment_modify_shared(dev_info->segment_name,
739- SEGMENT_EXCLUSIVE);
740- if (rc < 0) {
741- BUG_ON(rc == -EINVAL);
742- if (rc != -EAGAIN)
743- goto removeseg;
744- } else {
745- dev_info->is_shared = 0;
746- set_disk_ro(dev_info->gd, 0);
747+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
748+ rc = segment_modify_shared(entry->segment_name,
749+ SEGMENT_EXCLUSIVE);
750+ if (rc < 0) {
751+ BUG_ON(rc == -EINVAL);
752+ if (rc != -EAGAIN)
753+ goto removeseg;
754+ }
755 }
756+ dev_info->is_shared = 0;
757+ set_disk_ro(dev_info->gd, 0);
758 } else {
759 rc = -EINVAL;
760 goto out;
761@@ -220,8 +404,14 @@ dcssblk_shared_store(struct device *dev,
762 goto out;
763
764 removeseg:
765- PRINT_ERR("Could not reload segment %s, removing it now!\n",
766- dev_info->segment_name);
767+ PRINT_ERR("Could not reload segment(s) of the device %s, removing "
768+ "segment(s) now!\n",
769+ dev_info->segment_name);
770+ temp = entry;
771+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
772+ if (entry != temp)
773+ segment_unload(entry->segment_name);
774+ }
775 list_del(&dev_info->lh);
776
777 del_gendisk(dev_info->gd);
778@@ -254,6 +444,7 @@ static ssize_t
779 dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
780 {
781 struct dcssblk_dev_info *dev_info;
782+ struct segment_info *entry;
783
784 if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
785 return -EINVAL;
786@@ -263,14 +454,16 @@ dcssblk_save_store(struct device *dev, s
787 if (inbuf[0] == '1') {
788 if (atomic_read(&dev_info->use_count) == 0) {
789 // device is idle => we save immediately
790- PRINT_INFO("Saving segment %s\n",
791+ PRINT_INFO("Saving segment(s) of the device %s\n",
792 dev_info->segment_name);
793- segment_save(dev_info->segment_name);
794+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
795+ segment_save(entry->segment_name);
796+ }
797 } else {
798 // device is busy => we save it when it becomes
799 // idle in dcssblk_release
800- PRINT_INFO("Segment %s is currently busy, it will "
801- "be saved when it becomes idle...\n",
802+ PRINT_INFO("Device %s is currently busy, segment(s) "
803+ "will be saved when it becomes idle...\n",
804 dev_info->segment_name);
805 dev_info->save_pending = 1;
806 }
807@@ -279,7 +472,8 @@ dcssblk_save_store(struct device *dev, s
808 // device is busy & the user wants to undo his save
809 // request
810 dev_info->save_pending = 0;
811- PRINT_INFO("Pending save for segment %s deactivated\n",
812+ PRINT_INFO("Pending save for segment(s) of the device "
813+ "%s deactivated\n",
814 dev_info->segment_name);
815 }
816 } else {
817@@ -291,66 +485,123 @@ dcssblk_save_store(struct device *dev, s
818 }
819
820 /*
821+ * device attribute for showing all segments in a device
822+ */
823+static ssize_t
824+dcssblk_seglist_show(struct device *dev, struct device_attribute *attr,
825+ char *buf)
826+{
827+ int i;
828+
829+ struct dcssblk_dev_info *dev_info;
830+ struct segment_info *entry;
831+
832+ down_read(&dcssblk_devices_sem);
833+ dev_info = container_of(dev, struct dcssblk_dev_info, dev);
834+ i = 0;
835+ buf[0] = '\0';
836+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
837+ strcpy(&buf[i], entry->segment_name);
838+ i += strlen(entry->segment_name);
839+ buf[i] = '\n';
840+ i++;
841+ }
842+ up_read(&dcssblk_devices_sem);
843+ return i;
844+}
845+
846+/*
847 * device attribute for adding devices
848 */
849 static ssize_t
850 dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
851 {
852- int rc, i;
853+ int rc, i, j, num_of_segments;
854 struct dcssblk_dev_info *dev_info;
855+ struct segment_info *seg_info, *temp;
856 char *local_buf;
857 unsigned long seg_byte_size;
858
859 dev_info = NULL;
860+ seg_info = NULL;
861 if (dev != dcssblk_root_dev) {
862 rc = -EINVAL;
863 goto out_nobuf;
864 }
865+ if ((count < 1) || (buf[0] == '\0') || (buf[0] == '\n')) {
866+ rc = -ENAMETOOLONG;
867+ goto out_nobuf;
868+ }
869+
870 local_buf = kmalloc(count + 1, GFP_KERNEL);
871 if (local_buf == NULL) {
872 rc = -ENOMEM;
873 goto out_nobuf;
874 }
875+
876 /*
877 * parse input
878 */
879+ num_of_segments = 0;
880 for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) {
881- local_buf[i] = toupper(buf[i]);
882+ for (j = i; (buf[j] != ':') &&
883+ (buf[j] != '\0') &&
884+ (buf[j] != '\n') &&
885+ j < count; j++) {
886+ local_buf[j-i] = toupper(buf[j]);
887+ }
888+ local_buf[j-i] = '\0';
889+ if (((j - i) == 0) || ((j - i) > 8)) {
890+ rc = -ENAMETOOLONG;
891+ goto seg_list_del;
892+ }
893+
894+ rc = dcssblk_load_segment(local_buf, &seg_info);
895+ if (rc < 0)
896+ goto seg_list_del;
897+ /*
898+ * get a struct dcssblk_dev_info
899+ */
900+ if (num_of_segments == 0) {
901+ dev_info = kzalloc(sizeof(struct dcssblk_dev_info),
902+ GFP_KERNEL);
903+ if (dev_info == NULL) {
904+ rc = -ENOMEM;
905+ goto out;
906+ }
907+ strcpy(dev_info->segment_name, local_buf);
908+ dev_info->segment_type = seg_info->segment_type;
909+ INIT_LIST_HEAD(&dev_info->seg_list);
910+ }
911+ list_add_tail(&seg_info->lh, &dev_info->seg_list);
912+ num_of_segments++;
913+ i = j;
914+
915+ if ((buf[j] == '\0') || (buf[j] == '\n'))
916+ break;
917 }
918- local_buf[i] = '\0';
919- if ((i == 0) || (i > 8)) {
920+
921+ /* no trailing colon at the end of the input */
922+ if ((i > 0) && (buf[i-1] == ':')) {
923 rc = -ENAMETOOLONG;
924- goto out;
925- }
926- /*
927- * already loaded?
928- */
929- down_read(&dcssblk_devices_sem);
930- dev_info = dcssblk_get_device_by_name(local_buf);
931- up_read(&dcssblk_devices_sem);
932- if (dev_info != NULL) {
933- PRINT_WARN("Segment %s already loaded!\n", local_buf);
934- rc = -EEXIST;
935- goto out;
936- }
937- /*
938- * get a struct dcssblk_dev_info
939- */
940- dev_info = kzalloc(sizeof(struct dcssblk_dev_info), GFP_KERNEL);
941- if (dev_info == NULL) {
942- rc = -ENOMEM;
943- goto out;
944+ goto seg_list_del;
945 }
946+ strlcpy(local_buf, buf, i + 1);
947+ dev_info->num_of_segments = num_of_segments;
948+ rc = dcssblk_is_continuous(dev_info);
949+ if (rc < 0)
950+ goto seg_list_del;
951
952- strcpy(dev_info->segment_name, local_buf);
953- strlcpy(dev_info->dev.bus_id, local_buf, BUS_ID_SIZE);
954+ dev_info->start = dcssblk_find_lowest_addr(dev_info);
955+ dev_info->end = dcssblk_find_highest_addr(dev_info);
956+
957+ strlcpy(dev_info->dev.bus_id, dev_info->segment_name, BUS_ID_SIZE);
958 dev_info->dev.release = dcssblk_release_segment;
959 INIT_LIST_HEAD(&dev_info->lh);
960-
961 dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK);
962 if (dev_info->gd == NULL) {
963 rc = -ENOMEM;
964- goto free_dev_info;
965+ goto seg_list_del;
966 }
967 dev_info->gd->major = dcssblk_major;
968 dev_info->gd->fops = &dcssblk_devops;
969@@ -360,59 +611,43 @@ dcssblk_add_store(struct device *dev, st
970 dev_info->gd->driverfs_dev = &dev_info->dev;
971 blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
972 blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096);
973- /*
974- * load the segment
975- */
976- rc = segment_load(local_buf, SEGMENT_SHARED,
977- &dev_info->start, &dev_info->end);
978- if (rc < 0) {
979- segment_warning(rc, dev_info->segment_name);
980- goto dealloc_gendisk;
981- }
982+
983 seg_byte_size = (dev_info->end - dev_info->start + 1);
984 set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors
985- PRINT_INFO("Loaded segment %s, size = %lu Byte, "
986+ PRINT_INFO("Loaded segment(s) %s, size = %lu Byte, "
987 "capacity = %lu (512 Byte) sectors\n", local_buf,
988 seg_byte_size, seg_byte_size >> 9);
989
990- dev_info->segment_type = rc;
991 dev_info->save_pending = 0;
992 dev_info->is_shared = 1;
993 dev_info->dev.parent = dcssblk_root_dev;
994
995 /*
996- * get minor, add to list
997+ *get minor, add to list
998 */
999 down_write(&dcssblk_devices_sem);
1000- if (dcssblk_get_device_by_name(local_buf)) {
1001- up_write(&dcssblk_devices_sem);
1002+ if (dcssblk_get_segment_by_name(local_buf)) {
1003 rc = -EEXIST;
1004- goto unload_seg;
1005+ goto release_gd;
1006 }
1007 rc = dcssblk_assign_free_minor(dev_info);
1008- if (rc) {
1009- up_write(&dcssblk_devices_sem);
1010- PRINT_ERR("No free minor number available! "
1011- "Unloading segment...\n");
1012- goto unload_seg;
1013- }
1014+ if (rc)
1015+ goto release_gd;
1016 sprintf(dev_info->gd->disk_name, "dcssblk%d",
1017 dev_info->gd->first_minor);
1018 list_add_tail(&dev_info->lh, &dcssblk_devices);
1019
1020 if (!try_module_get(THIS_MODULE)) {
1021 rc = -ENODEV;
1022- goto list_del;
1023+ goto dev_list_del;
1024 }
1025 /*
1026 * register the device
1027 */
1028 rc = device_register(&dev_info->dev);
1029 if (rc) {
1030- PRINT_ERR("Segment %s could not be registered RC=%d\n",
1031- local_buf, rc);
1032 module_put(THIS_MODULE);
1033- goto list_del;
1034+ goto dev_list_del;
1035 }
1036 get_device(&dev_info->dev);
1037 rc = device_create_file(&dev_info->dev, &dev_attr_shared);
1038@@ -421,6 +656,9 @@ dcssblk_add_store(struct device *dev, st
1039 rc = device_create_file(&dev_info->dev, &dev_attr_save);
1040 if (rc)
1041 goto unregister_dev;
1042+ rc = device_create_file(&dev_info->dev, &dev_attr_seglist);
1043+ if (rc)
1044+ goto unregister_dev;
1045
1046 add_disk(dev_info->gd);
1047
1048@@ -434,7 +672,6 @@ dcssblk_add_store(struct device *dev, st
1049 set_disk_ro(dev_info->gd,0);
1050 break;
1051 }
1052- PRINT_DEBUG("Segment %s loaded successfully\n", local_buf);
1053 up_write(&dcssblk_devices_sem);
1054 rc = count;
1055 goto out;
1056@@ -445,20 +682,27 @@ unregister_dev:
1057 dev_info->gd->queue = NULL;
1058 put_disk(dev_info->gd);
1059 device_unregister(&dev_info->dev);
1060- segment_unload(dev_info->segment_name);
1061+ list_for_each_entry(seg_info, &dev_info->seg_list, lh) {
1062+ segment_unload(seg_info->segment_name);
1063+ }
1064 put_device(&dev_info->dev);
1065 up_write(&dcssblk_devices_sem);
1066 goto out;
1067-list_del:
1068+dev_list_del:
1069 list_del(&dev_info->lh);
1070- up_write(&dcssblk_devices_sem);
1071-unload_seg:
1072- segment_unload(local_buf);
1073-dealloc_gendisk:
1074+release_gd:
1075 blk_cleanup_queue(dev_info->dcssblk_queue);
1076 dev_info->gd->queue = NULL;
1077 put_disk(dev_info->gd);
1078-free_dev_info:
1079+ up_write(&dcssblk_devices_sem);
1080+seg_list_del:
1081+ if (dev_info == NULL)
1082+ goto out;
1083+ list_for_each_entry_safe(seg_info, temp, &dev_info->seg_list, lh) {
1084+ list_del(&seg_info->lh);
1085+ segment_unload(seg_info->segment_name);
1086+ kfree(seg_info);
1087+ }
1088 kfree(dev_info);
1089 out:
1090 kfree(local_buf);
1091@@ -473,6 +717,7 @@ static ssize_t
1092 dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
1093 {
1094 struct dcssblk_dev_info *dev_info;
1095+ struct segment_info *entry;
1096 int rc, i;
1097 char *local_buf;
1098
1099@@ -499,26 +744,28 @@ dcssblk_remove_store(struct device *dev,
1100 dev_info = dcssblk_get_device_by_name(local_buf);
1101 if (dev_info == NULL) {
1102 up_write(&dcssblk_devices_sem);
1103- PRINT_WARN("Segment %s is not loaded!\n", local_buf);
1104+ PRINT_WARN("Device %s is not loaded!\n", local_buf);
1105 rc = -ENODEV;
1106 goto out_buf;
1107 }
1108 if (atomic_read(&dev_info->use_count) != 0) {
1109 up_write(&dcssblk_devices_sem);
1110- PRINT_WARN("Segment %s is in use!\n", local_buf);
1111+ PRINT_WARN("Device %s is in use!\n", local_buf);
1112 rc = -EBUSY;
1113 goto out_buf;
1114 }
1115- list_del(&dev_info->lh);
1116
1117+ list_del(&dev_info->lh);
1118 del_gendisk(dev_info->gd);
1119 blk_cleanup_queue(dev_info->dcssblk_queue);
1120 dev_info->gd->queue = NULL;
1121 put_disk(dev_info->gd);
1122 device_unregister(&dev_info->dev);
1123- segment_unload(dev_info->segment_name);
1124- PRINT_DEBUG("Segment %s unloaded successfully\n",
1125- dev_info->segment_name);
1126+
1127+ /* unload all related segments */
1128+ list_for_each_entry(entry, &dev_info->seg_list, lh)
1129+ segment_unload(entry->segment_name);
1130+
1131 put_device(&dev_info->dev);
1132 up_write(&dcssblk_devices_sem);
1133
1134@@ -550,6 +797,7 @@ static int
1135 dcssblk_release(struct inode *inode, struct file *filp)
1136 {
1137 struct dcssblk_dev_info *dev_info;
1138+ struct segment_info *entry;
1139 int rc;
1140
1141 dev_info = inode->i_bdev->bd_disk->private_data;
1142@@ -560,9 +808,11 @@ dcssblk_release(struct inode *inode, str
1143 down_write(&dcssblk_devices_sem);
1144 if (atomic_dec_and_test(&dev_info->use_count)
1145 && (dev_info->save_pending)) {
1146- PRINT_INFO("Segment %s became idle and is being saved now\n",
1147+ PRINT_INFO("Device %s became idle and is being saved now\n",
1148 dev_info->segment_name);
1149- segment_save(dev_info->segment_name);
1150+ list_for_each_entry(entry, &dev_info->seg_list, lh) {
1151+ segment_save(entry->segment_name);
1152+ }
1153 dev_info->save_pending = 0;
1154 }
1155 up_write(&dcssblk_devices_sem);
1156@@ -602,7 +852,8 @@ dcssblk_make_request(struct request_queu
1157 case SEG_TYPE_SC:
1158 /* cannot write to these segments */
1159 if (bio_data_dir(bio) == WRITE) {
1160- PRINT_WARN("rejecting write to ro segment %s\n", dev_info->dev.bus_id);
1161+ PRINT_WARN("rejecting write to ro device %s\n",
1162+ dev_info->dev.bus_id);
1163 goto fail;
1164 }
1165 }
1166@@ -657,7 +908,7 @@ static void
1167 dcssblk_check_params(void)
1168 {
1169 int rc, i, j, k;
1170- char buf[9];
1171+ char buf[DCSSBLK_PARM_LEN + 1];
1172 struct dcssblk_dev_info *dev_info;
1173
1174 for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0');
1175@@ -665,15 +916,16 @@ dcssblk_check_params(void)
1176 for (j = i; (dcssblk_segments[j] != ',') &&
1177 (dcssblk_segments[j] != '\0') &&
1178 (dcssblk_segments[j] != '(') &&
1179- (j - i) < 8; j++)
1180+ (j < DCSSBLK_PARM_LEN); j++)
1181 {
1182 buf[j-i] = dcssblk_segments[j];
1183 }
1184 buf[j-i] = '\0';
1185 rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i);
1186 if ((rc >= 0) && (dcssblk_segments[j] == '(')) {
1187- for (k = 0; buf[k] != '\0'; k++)
1188+ for (k = 0; (buf[k] != ':') && (buf[k] != '\0'); k++)
1189 buf[k] = toupper(buf[k]);
1190+ buf[k] = '\0';
1191 if (!strncmp(&dcssblk_segments[j], "(local)", 7)) {
1192 down_read(&dcssblk_devices_sem);
1193 dev_info = dcssblk_get_device_by_name(buf);
1194@@ -740,10 +992,12 @@ module_exit(dcssblk_exit);
1195
1196 module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444);
1197 MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, "
1198- "comma-separated list, each name max. 8 chars.\n"
1199- "Adding \"(local)\" to segment name equals echoing 0 to "
1200- "/sys/devices/dcssblk/<segment name>/shared after loading "
1201- "the segment - \n"
1202- "e.g. segments=\"mydcss1,mydcss2,mydcss3(local)\"");
1203+ "comma-separated list, names in each set separated "
1204+ "by commas are separated by colons, each set contains "
1205+ "names of contiguous segments and each name max. 8 chars.\n"
1206+ "Adding \"(local)\" to the end of each set equals echoing 0 "
1207+ "to /sys/devices/dcssblk/<device name>/shared after loading "
1208+ "the contiguous segments - \n"
1209+ "e.g. segments=\"mydcss1,mydcss2:mydcss3,mydcss4(local)\"");
1210
1211 MODULE_LICENSE("GPL");