xref: /linux/arch/s390/mm/extmem.c (revision 8fa5723aa7e053d498336b48448b292fc2e0458b)
1 /*
2  * File...........: arch/s390/mm/extmem.c
3  * Author(s)......: Carsten Otte <cotte@de.ibm.com>
4  * 		    Rob M van der Heij <rvdheij@nl.ibm.com>
5  * 		    Steven Shultz <shultzss@us.ibm.com>
6  * Bugreports.to..: <Linux390@de.ibm.com>
7  * (C) IBM Corporation 2002-2004
8  */
9 
10 #include <linux/kernel.h>
11 #include <linux/string.h>
12 #include <linux/spinlock.h>
13 #include <linux/list.h>
14 #include <linux/slab.h>
15 #include <linux/module.h>
16 #include <linux/bootmem.h>
17 #include <linux/ctype.h>
18 #include <linux/ioport.h>
19 #include <asm/page.h>
20 #include <asm/pgtable.h>
21 #include <asm/ebcdic.h>
22 #include <asm/errno.h>
23 #include <asm/extmem.h>
24 #include <asm/cpcmd.h>
25 #include <asm/setup.h>
26 
27 #define DCSS_DEBUG	/* Debug messages on/off */
28 
29 #define DCSS_NAME "extmem"
30 #ifdef DCSS_DEBUG
31 #define PRINT_DEBUG(x...)	printk(KERN_DEBUG DCSS_NAME " debug:" x)
32 #else
33 #define PRINT_DEBUG(x...)   do {} while (0)
34 #endif
35 #define PRINT_INFO(x...)	printk(KERN_INFO DCSS_NAME " info:" x)
36 #define PRINT_WARN(x...)	printk(KERN_WARNING DCSS_NAME " warning:" x)
37 #define PRINT_ERR(x...)		printk(KERN_ERR DCSS_NAME " error:" x)
38 
39 
40 #define DCSS_LOADSHR    0x00
41 #define DCSS_LOADNSR    0x04
42 #define DCSS_PURGESEG   0x08
43 #define DCSS_FINDSEG    0x0c
44 #define DCSS_LOADNOLY   0x10
45 #define DCSS_SEGEXT     0x18
46 #define DCSS_LOADSHRX	0x20
47 #define DCSS_LOADNSRX	0x24
48 #define DCSS_FINDSEGX	0x2c
49 #define DCSS_SEGEXTX	0x38
50 #define DCSS_FINDSEGA   0x0c
51 
52 struct qrange {
53 	unsigned long  start; /* last byte type */
54 	unsigned long  end;   /* last byte reserved */
55 };
56 
57 struct qout64 {
58 	unsigned long segstart;
59 	unsigned long segend;
60 	int segcnt;
61 	int segrcnt;
62 	struct qrange range[6];
63 };
64 
65 #ifdef CONFIG_64BIT
66 struct qrange_old {
67 	unsigned int start; /* last byte type */
68 	unsigned int end;   /* last byte reserved */
69 };
70 
71 /* output area format for the Diag x'64' old subcode x'18' */
72 struct qout64_old {
73 	int segstart;
74 	int segend;
75 	int segcnt;
76 	int segrcnt;
77 	struct qrange_old range[6];
78 };
79 #endif
80 
81 struct qin64 {
82 	char qopcode;
83 	char rsrv1[3];
84 	char qrcode;
85 	char rsrv2[3];
86 	char qname[8];
87 	unsigned int qoutptr;
88 	short int qoutlen;
89 };
90 
91 struct dcss_segment {
92 	struct list_head list;
93 	char dcss_name[8];
94 	char res_name[15];
95 	unsigned long start_addr;
96 	unsigned long end;
97 	atomic_t ref_count;
98 	int do_nonshared;
99 	unsigned int vm_segtype;
100 	struct qrange range[6];
101 	int segcnt;
102 	struct resource *res;
103 };
104 
105 static DEFINE_MUTEX(dcss_lock);
106 static LIST_HEAD(dcss_list);
107 static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
108 					"EW/EN-MIXED" };
109 static int loadshr_scode, loadnsr_scode, findseg_scode;
110 static int segext_scode, purgeseg_scode;
111 static int scode_set;
112 
113 /* set correct Diag x'64' subcodes. */
114 static int
115 dcss_set_subcodes(void)
116 {
117 #ifdef CONFIG_64BIT
118 	char *name = kmalloc(8 * sizeof(char), GFP_DMA);
119 	unsigned long rx, ry;
120 	int rc;
121 
122 	if (name == NULL)
123 		return -ENOMEM;
124 
125 	rx = (unsigned long) name;
126 	ry = DCSS_FINDSEGX;
127 
128 	strcpy(name, "dummy");
129 	asm volatile(
130 		"	diag	%0,%1,0x64\n"
131 		"0:	ipm	%2\n"
132 		"	srl	%2,28\n"
133 		"	j	2f\n"
134 		"1:	la	%2,3\n"
135 		"2:\n"
136 		EX_TABLE(0b, 1b)
137 		: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
138 
139 	kfree(name);
140 	/* Diag x'64' new subcodes are supported, set to new subcodes */
141 	if (rc != 3) {
142 		loadshr_scode = DCSS_LOADSHRX;
143 		loadnsr_scode = DCSS_LOADNSRX;
144 		purgeseg_scode = DCSS_PURGESEG;
145 		findseg_scode = DCSS_FINDSEGX;
146 		segext_scode = DCSS_SEGEXTX;
147 		return 0;
148 	}
149 #endif
150 	/* Diag x'64' new subcodes are not supported, set to old subcodes */
151 	loadshr_scode = DCSS_LOADNOLY;
152 	loadnsr_scode = DCSS_LOADNSR;
153 	purgeseg_scode = DCSS_PURGESEG;
154 	findseg_scode = DCSS_FINDSEG;
155 	segext_scode = DCSS_SEGEXT;
156 	return 0;
157 }
158 
159 /*
160  * Create the 8 bytes, ebcdic VM segment name from
161  * an ascii name.
162  */
163 static void
164 dcss_mkname(char *name, char *dcss_name)
165 {
166 	int i;
167 
168 	for (i = 0; i < 8; i++) {
169 		if (name[i] == '\0')
170 			break;
171 		dcss_name[i] = toupper(name[i]);
172 	};
173 	for (; i < 8; i++)
174 		dcss_name[i] = ' ';
175 	ASCEBC(dcss_name, 8);
176 }
177 
178 
179 /*
180  * search all segments in dcss_list, and return the one
181  * namend *name. If not found, return NULL.
182  */
183 static struct dcss_segment *
184 segment_by_name (char *name)
185 {
186 	char dcss_name[9];
187 	struct list_head *l;
188 	struct dcss_segment *tmp, *retval = NULL;
189 
190 	BUG_ON(!mutex_is_locked(&dcss_lock));
191 	dcss_mkname (name, dcss_name);
192 	list_for_each (l, &dcss_list) {
193 		tmp = list_entry (l, struct dcss_segment, list);
194 		if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) {
195 			retval = tmp;
196 			break;
197 		}
198 	}
199 	return retval;
200 }
201 
202 
203 /*
204  * Perform a function on a dcss segment.
205  */
206 static inline int
207 dcss_diag(int *func, void *parameter,
208            unsigned long *ret1, unsigned long *ret2)
209 {
210 	unsigned long rx, ry;
211 	int rc;
212 
213 	if (scode_set == 0) {
214 		rc = dcss_set_subcodes();
215 		if (rc < 0)
216 			return rc;
217 		scode_set = 1;
218 	}
219 	rx = (unsigned long) parameter;
220 	ry = (unsigned long) *func;
221 
222 #ifdef CONFIG_64BIT
223 	/* 64-bit Diag x'64' new subcode, keep in 64-bit addressing mode */
224 	if (*func > DCSS_SEGEXT)
225 		asm volatile(
226 			"	diag	%0,%1,0x64\n"
227 			"	ipm	%2\n"
228 			"	srl	%2,28\n"
229 			: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
230 	/* 31-bit Diag x'64' old subcode, switch to 31-bit addressing mode */
231 	else
232 		asm volatile(
233 			"	sam31\n"
234 			"	diag	%0,%1,0x64\n"
235 			"	sam64\n"
236 			"	ipm	%2\n"
237 			"	srl	%2,28\n"
238 			: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
239 #else
240 	asm volatile(
241 		"	diag	%0,%1,0x64\n"
242 		"	ipm	%2\n"
243 		"	srl	%2,28\n"
244 		: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
245 #endif
246 	*ret1 = rx;
247 	*ret2 = ry;
248 	return rc;
249 }
250 
251 static inline int
252 dcss_diag_translate_rc (int vm_rc) {
253 	if (vm_rc == 44)
254 		return -ENOENT;
255 	return -EIO;
256 }
257 
258 
259 /* do a diag to get info about a segment.
260  * fills start_address, end and vm_segtype fields
261  */
262 static int
263 query_segment_type (struct dcss_segment *seg)
264 {
265 	struct qin64  *qin = kmalloc (sizeof(struct qin64), GFP_DMA);
266 	struct qout64 *qout = kmalloc (sizeof(struct qout64), GFP_DMA);
267 
268 	int diag_cc, rc, i;
269 	unsigned long dummy, vmrc;
270 
271 	if ((qin == NULL) || (qout == NULL)) {
272 		rc = -ENOMEM;
273 		goto out_free;
274 	}
275 
276 	/* initialize diag input parameters */
277 	qin->qopcode = DCSS_FINDSEGA;
278 	qin->qoutptr = (unsigned long) qout;
279 	qin->qoutlen = sizeof(struct qout64);
280 	memcpy (qin->qname, seg->dcss_name, 8);
281 
282 	diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc);
283 
284 	if (diag_cc < 0) {
285 		rc = diag_cc;
286 		goto out_free;
287 	}
288 	if (diag_cc > 1) {
289 		PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc);
290 		rc = dcss_diag_translate_rc (vmrc);
291 		goto out_free;
292 	}
293 
294 #ifdef CONFIG_64BIT
295 	/* Only old format of output area of Diagnose x'64' is supported,
296 	   copy data for the new format. */
297 	if (segext_scode == DCSS_SEGEXT) {
298 		struct qout64_old *qout_old;
299 		qout_old = kzalloc(sizeof(struct qout64_old), GFP_DMA);
300 		if (qout_old == NULL) {
301 			rc = -ENOMEM;
302 			goto out_free;
303 		}
304 		memcpy(qout_old, qout, sizeof(struct qout64_old));
305 		qout->segstart = (unsigned long) qout_old->segstart;
306 		qout->segend = (unsigned long) qout_old->segend;
307 		qout->segcnt = qout_old->segcnt;
308 		qout->segrcnt = qout_old->segrcnt;
309 
310 		if (qout->segcnt > 6)
311 			qout->segrcnt = 6;
312 		for (i = 0; i < qout->segrcnt; i++) {
313 			qout->range[i].start =
314 				(unsigned long) qout_old->range[i].start;
315 			qout->range[i].end =
316 				(unsigned long) qout_old->range[i].end;
317 		}
318 		kfree(qout_old);
319 	}
320 #endif
321 	if (qout->segcnt > 6) {
322 		rc = -ENOTSUPP;
323 		goto out_free;
324 	}
325 
326 	if (qout->segcnt == 1) {
327 		seg->vm_segtype = qout->range[0].start & 0xff;
328 	} else {
329 		/* multi-part segment. only one type supported here:
330 		    - all parts are contiguous
331 		    - all parts are either EW or EN type
332 		    - maximum 6 parts allowed */
333 		unsigned long start = qout->segstart >> PAGE_SHIFT;
334 		for (i=0; i<qout->segcnt; i++) {
335 			if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) &&
336 			    ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) {
337 				rc = -ENOTSUPP;
338 				goto out_free;
339 			}
340 			if (start != qout->range[i].start >> PAGE_SHIFT) {
341 				rc = -ENOTSUPP;
342 				goto out_free;
343 			}
344 			start = (qout->range[i].end >> PAGE_SHIFT) + 1;
345 		}
346 		seg->vm_segtype = SEG_TYPE_EWEN;
347 	}
348 
349 	/* analyze diag output and update seg */
350 	seg->start_addr = qout->segstart;
351 	seg->end = qout->segend;
352 
353 	memcpy (seg->range, qout->range, 6*sizeof(struct qrange));
354 	seg->segcnt = qout->segcnt;
355 
356 	rc = 0;
357 
358  out_free:
359 	kfree(qin);
360 	kfree(qout);
361 	return rc;
362 }
363 
364 /*
365  * get info about a segment
366  * possible return values:
367  * -ENOSYS  : we are not running on VM
368  * -EIO     : could not perform query diagnose
369  * -ENOENT  : no such segment
370  * -ENOTSUPP: multi-part segment cannot be used with linux
371  * -ENOSPC  : segment cannot be used (overlaps with storage)
372  * -ENOMEM  : out of memory
373  * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
374  */
375 int
376 segment_type (char* name)
377 {
378 	int rc;
379 	struct dcss_segment seg;
380 
381 	if (!MACHINE_IS_VM)
382 		return -ENOSYS;
383 
384 	dcss_mkname(name, seg.dcss_name);
385 	rc = query_segment_type (&seg);
386 	if (rc < 0)
387 		return rc;
388 	return seg.vm_segtype;
389 }
390 
391 /*
392  * check if segment collides with other segments that are currently loaded
393  * returns 1 if this is the case, 0 if no collision was found
394  */
395 static int
396 segment_overlaps_others (struct dcss_segment *seg)
397 {
398 	struct list_head *l;
399 	struct dcss_segment *tmp;
400 
401 	BUG_ON(!mutex_is_locked(&dcss_lock));
402 	list_for_each(l, &dcss_list) {
403 		tmp = list_entry(l, struct dcss_segment, list);
404 		if ((tmp->start_addr >> 20) > (seg->end >> 20))
405 			continue;
406 		if ((tmp->end >> 20) < (seg->start_addr >> 20))
407 			continue;
408 		if (seg == tmp)
409 			continue;
410 		return 1;
411 	}
412 	return 0;
413 }
414 
415 /*
416  * real segment loading function, called from segment_load
417  */
418 static int
419 __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end)
420 {
421 	struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment),
422 			GFP_DMA);
423 	int rc, diag_cc;
424 	unsigned long start_addr, end_addr, dummy;
425 
426 	if (seg == NULL) {
427 		rc = -ENOMEM;
428 		goto out;
429 	}
430 	dcss_mkname (name, seg->dcss_name);
431 	rc = query_segment_type (seg);
432 	if (rc < 0)
433 		goto out_free;
434 
435 	if (loadshr_scode == DCSS_LOADSHRX) {
436 		if (segment_overlaps_others(seg)) {
437 			rc = -EBUSY;
438 			goto out_free;
439 		}
440 	}
441 
442 	rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
443 
444 	if (rc)
445 		goto out_free;
446 
447 	seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL);
448 	if (seg->res == NULL) {
449 		rc = -ENOMEM;
450 		goto out_shared;
451 	}
452 	seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM;
453 	seg->res->start = seg->start_addr;
454 	seg->res->end = seg->end;
455 	memcpy(&seg->res_name, seg->dcss_name, 8);
456 	EBCASC(seg->res_name, 8);
457 	seg->res_name[8] = '\0';
458 	strncat(seg->res_name, " (DCSS)", 7);
459 	seg->res->name = seg->res_name;
460 	rc = seg->vm_segtype;
461 	if (rc == SEG_TYPE_SC ||
462 	    ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared))
463 		seg->res->flags |= IORESOURCE_READONLY;
464 	if (request_resource(&iomem_resource, seg->res)) {
465 		rc = -EBUSY;
466 		kfree(seg->res);
467 		goto out_shared;
468 	}
469 
470 	if (do_nonshared)
471 		diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
472 				&start_addr, &end_addr);
473 	else
474 		diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
475 				&start_addr, &end_addr);
476 	if (diag_cc < 0) {
477 		dcss_diag(&purgeseg_scode, seg->dcss_name,
478 				&dummy, &dummy);
479 		rc = diag_cc;
480 		goto out_resource;
481 	}
482 	if (diag_cc > 1) {
483 		PRINT_WARN ("segment_load: could not load segment %s - "
484 				"diag returned error (%ld)\n",
485 				name, end_addr);
486 		rc = dcss_diag_translate_rc(end_addr);
487 		dcss_diag(&purgeseg_scode, seg->dcss_name,
488 				&dummy, &dummy);
489 		goto out_resource;
490 	}
491 	seg->start_addr = start_addr;
492 	seg->end = end_addr;
493 	seg->do_nonshared = do_nonshared;
494 	atomic_set(&seg->ref_count, 1);
495 	list_add(&seg->list, &dcss_list);
496 	*addr = seg->start_addr;
497 	*end  = seg->end;
498 	if (do_nonshared)
499 		PRINT_INFO ("segment_load: loaded segment %s range %p .. %p "
500 				"type %s in non-shared mode\n", name,
501 				(void*)seg->start_addr, (void*)seg->end,
502 				segtype_string[seg->vm_segtype]);
503 	else {
504 		PRINT_INFO ("segment_load: loaded segment %s range %p .. %p "
505 				"type %s in shared mode\n", name,
506 				(void*)seg->start_addr, (void*)seg->end,
507 				segtype_string[seg->vm_segtype]);
508 	}
509 	goto out;
510  out_resource:
511 	release_resource(seg->res);
512 	kfree(seg->res);
513  out_shared:
514 	vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
515  out_free:
516 	kfree(seg);
517  out:
518 	return rc;
519 }
520 
521 /*
522  * this function loads a DCSS segment
523  * name         : name of the DCSS
524  * do_nonshared : 0 indicates that the dcss should be shared with other linux images
525  *                1 indicates that the dcss should be exclusive for this linux image
526  * addr         : will be filled with start address of the segment
527  * end          : will be filled with end address of the segment
528  * return values:
529  * -ENOSYS  : we are not running on VM
530  * -EIO     : could not perform query or load diagnose
531  * -ENOENT  : no such segment
532  * -ENOTSUPP: multi-part segment cannot be used with linux
533  * -ENOSPC  : segment cannot be used (overlaps with storage)
534  * -EBUSY   : segment can temporarily not be used (overlaps with dcss)
535  * -ERANGE  : segment cannot be used (exceeds kernel mapping range)
536  * -EPERM   : segment is currently loaded with incompatible permissions
537  * -ENOMEM  : out of memory
538  * 0 .. 6   : type of segment as defined in include/asm-s390/extmem.h
539  */
540 int
541 segment_load (char *name, int do_nonshared, unsigned long *addr,
542 		unsigned long *end)
543 {
544 	struct dcss_segment *seg;
545 	int rc;
546 
547 	if (!MACHINE_IS_VM)
548 		return -ENOSYS;
549 
550 	mutex_lock(&dcss_lock);
551 	seg = segment_by_name (name);
552 	if (seg == NULL)
553 		rc = __segment_load (name, do_nonshared, addr, end);
554 	else {
555 		if (do_nonshared == seg->do_nonshared) {
556 			atomic_inc(&seg->ref_count);
557 			*addr = seg->start_addr;
558 			*end  = seg->end;
559 			rc    = seg->vm_segtype;
560 		} else {
561 			*addr = *end = 0;
562 			rc    = -EPERM;
563 		}
564 	}
565 	mutex_unlock(&dcss_lock);
566 	return rc;
567 }
568 
569 /*
570  * this function modifies the shared state of a DCSS segment. note that
571  * name         : name of the DCSS
572  * do_nonshared : 0 indicates that the dcss should be shared with other linux images
573  *                1 indicates that the dcss should be exclusive for this linux image
574  * return values:
575  * -EIO     : could not perform load diagnose (segment gone!)
576  * -ENOENT  : no such segment (segment gone!)
577  * -EAGAIN  : segment is in use by other exploiters, try later
578  * -EINVAL  : no segment with the given name is currently loaded - name invalid
579  * -EBUSY   : segment can temporarily not be used (overlaps with dcss)
580  * 0	    : operation succeeded
581  */
582 int
583 segment_modify_shared (char *name, int do_nonshared)
584 {
585 	struct dcss_segment *seg;
586 	unsigned long start_addr, end_addr, dummy;
587 	int rc, diag_cc;
588 
589 	mutex_lock(&dcss_lock);
590 	seg = segment_by_name (name);
591 	if (seg == NULL) {
592 		rc = -EINVAL;
593 		goto out_unlock;
594 	}
595 	if (do_nonshared == seg->do_nonshared) {
596 		PRINT_INFO ("segment_modify_shared: not reloading segment %s"
597 				" - already in requested mode\n",name);
598 		rc = 0;
599 		goto out_unlock;
600 	}
601 	if (atomic_read (&seg->ref_count) != 1) {
602 		PRINT_WARN ("segment_modify_shared: not reloading segment %s - "
603 				"segment is in use by other driver(s)\n",name);
604 		rc = -EAGAIN;
605 		goto out_unlock;
606 	}
607 	release_resource(seg->res);
608 	if (do_nonshared)
609 		seg->res->flags &= ~IORESOURCE_READONLY;
610 	else
611 		if (seg->vm_segtype == SEG_TYPE_SR ||
612 		    seg->vm_segtype == SEG_TYPE_ER)
613 			seg->res->flags |= IORESOURCE_READONLY;
614 
615 	if (request_resource(&iomem_resource, seg->res)) {
616 		PRINT_WARN("segment_modify_shared: could not reload segment %s"
617 			   " - overlapping resources\n", name);
618 		rc = -EBUSY;
619 		kfree(seg->res);
620 		goto out_del_mem;
621 	}
622 
623 	dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
624 	if (do_nonshared)
625 		diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
626 				&start_addr, &end_addr);
627 	else
628 		diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
629 				&start_addr, &end_addr);
630 	if (diag_cc < 0) {
631 		rc = diag_cc;
632 		goto out_del_res;
633 	}
634 	if (diag_cc > 1) {
635 		PRINT_WARN ("segment_modify_shared: could not reload segment %s"
636 				" - diag returned error (%ld)\n",
637 				name, end_addr);
638 		rc = dcss_diag_translate_rc(end_addr);
639 		goto out_del_res;
640 	}
641 	seg->start_addr = start_addr;
642 	seg->end = end_addr;
643 	seg->do_nonshared = do_nonshared;
644 	rc = 0;
645 	goto out_unlock;
646  out_del_res:
647 	release_resource(seg->res);
648 	kfree(seg->res);
649  out_del_mem:
650 	vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
651 	list_del(&seg->list);
652 	dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
653 	kfree(seg);
654  out_unlock:
655 	mutex_unlock(&dcss_lock);
656 	return rc;
657 }
658 
659 /*
660  * Decrease the use count of a DCSS segment and remove
661  * it from the address space if nobody is using it
662  * any longer.
663  */
664 void
665 segment_unload(char *name)
666 {
667 	unsigned long dummy;
668 	struct dcss_segment *seg;
669 
670 	if (!MACHINE_IS_VM)
671 		return;
672 
673 	mutex_lock(&dcss_lock);
674 	seg = segment_by_name (name);
675 	if (seg == NULL) {
676 		PRINT_ERR ("could not find segment %s in segment_unload, "
677 				"please report to linux390@de.ibm.com\n",name);
678 		goto out_unlock;
679 	}
680 	if (atomic_dec_return(&seg->ref_count) != 0)
681 		goto out_unlock;
682 	release_resource(seg->res);
683 	kfree(seg->res);
684 	vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
685 	list_del(&seg->list);
686 	dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
687 	kfree(seg);
688 out_unlock:
689 	mutex_unlock(&dcss_lock);
690 }
691 
692 /*
693  * save segment content permanently
694  */
695 void
696 segment_save(char *name)
697 {
698 	struct dcss_segment *seg;
699 	int startpfn = 0;
700 	int endpfn = 0;
701 	char cmd1[160];
702 	char cmd2[80];
703 	int i, response;
704 
705 	if (!MACHINE_IS_VM)
706 		return;
707 
708 	mutex_lock(&dcss_lock);
709 	seg = segment_by_name (name);
710 
711 	if (seg == NULL) {
712 		PRINT_ERR("could not find segment %s in segment_save, please "
713 			  "report to linux390@de.ibm.com\n", name);
714 		goto out;
715 	}
716 
717 	startpfn = seg->start_addr >> PAGE_SHIFT;
718 	endpfn = (seg->end) >> PAGE_SHIFT;
719 	sprintf(cmd1, "DEFSEG %s", name);
720 	for (i=0; i<seg->segcnt; i++) {
721 		sprintf(cmd1+strlen(cmd1), " %lX-%lX %s",
722 			seg->range[i].start >> PAGE_SHIFT,
723 			seg->range[i].end >> PAGE_SHIFT,
724 			segtype_string[seg->range[i].start & 0xff]);
725 	}
726 	sprintf(cmd2, "SAVESEG %s", name);
727 	response = 0;
728 	cpcmd(cmd1, NULL, 0, &response);
729 	if (response) {
730 		PRINT_ERR("segment_save: DEFSEG failed with response code %i\n",
731 			  response);
732 		goto out;
733 	}
734 	cpcmd(cmd2, NULL, 0, &response);
735 	if (response) {
736 		PRINT_ERR("segment_save: SAVESEG failed with response code %i\n",
737 			  response);
738 		goto out;
739 	}
740 out:
741 	mutex_unlock(&dcss_lock);
742 }
743 
744 /*
745  * print appropriate error message for segment_load()/segment_type()
746  * return code
747  */
748 void segment_warning(int rc, char *seg_name)
749 {
750 	switch (rc) {
751 	case -ENOENT:
752 		PRINT_WARN("cannot load/query segment %s, "
753 			   "does not exist\n", seg_name);
754 		break;
755 	case -ENOSYS:
756 		PRINT_WARN("cannot load/query segment %s, "
757 			   "not running on VM\n", seg_name);
758 		break;
759 	case -EIO:
760 		PRINT_WARN("cannot load/query segment %s, "
761 			   "hardware error\n", seg_name);
762 		break;
763 	case -ENOTSUPP:
764 		PRINT_WARN("cannot load/query segment %s, "
765 			   "is a multi-part segment\n", seg_name);
766 		break;
767 	case -ENOSPC:
768 		PRINT_WARN("cannot load/query segment %s, "
769 			   "overlaps with storage\n", seg_name);
770 		break;
771 	case -EBUSY:
772 		PRINT_WARN("cannot load/query segment %s, "
773 			   "overlaps with already loaded dcss\n", seg_name);
774 		break;
775 	case -EPERM:
776 		PRINT_WARN("cannot load/query segment %s, "
777 			   "already loaded in incompatible mode\n", seg_name);
778 		break;
779 	case -ENOMEM:
780 		PRINT_WARN("cannot load/query segment %s, "
781 			   "out of memory\n", seg_name);
782 		break;
783 	case -ERANGE:
784 		PRINT_WARN("cannot load/query segment %s, "
785 			   "exceeds kernel mapping range\n", seg_name);
786 		break;
787 	default:
788 		PRINT_WARN("cannot load/query segment %s, "
789 			   "return value %i\n", seg_name, rc);
790 		break;
791 	}
792 }
793 
794 EXPORT_SYMBOL(segment_load);
795 EXPORT_SYMBOL(segment_unload);
796 EXPORT_SYMBOL(segment_save);
797 EXPORT_SYMBOL(segment_type);
798 EXPORT_SYMBOL(segment_modify_shared);
799 EXPORT_SYMBOL(segment_warning);
800