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