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