.. | .. |
---|
16 | 16 | #include <linux/list.h> |
---|
17 | 17 | #include <linux/slab.h> |
---|
18 | 18 | #include <linux/export.h> |
---|
19 | | -#include <linux/bootmem.h> |
---|
| 19 | +#include <linux/memblock.h> |
---|
20 | 20 | #include <linux/ctype.h> |
---|
21 | 21 | #include <linux/ioport.h> |
---|
| 22 | +#include <linux/refcount.h> |
---|
| 23 | +#include <linux/pgtable.h> |
---|
22 | 24 | #include <asm/diag.h> |
---|
23 | 25 | #include <asm/page.h> |
---|
24 | | -#include <asm/pgtable.h> |
---|
25 | 26 | #include <asm/ebcdic.h> |
---|
26 | 27 | #include <asm/errno.h> |
---|
27 | 28 | #include <asm/extmem.h> |
---|
28 | 29 | #include <asm/cpcmd.h> |
---|
29 | 30 | #include <asm/setup.h> |
---|
30 | 31 | |
---|
31 | | -#define DCSS_LOADSHR 0x00 |
---|
32 | | -#define DCSS_LOADNSR 0x04 |
---|
33 | 32 | #define DCSS_PURGESEG 0x08 |
---|
34 | | -#define DCSS_FINDSEG 0x0c |
---|
35 | | -#define DCSS_LOADNOLY 0x10 |
---|
36 | | -#define DCSS_SEGEXT 0x18 |
---|
37 | 33 | #define DCSS_LOADSHRX 0x20 |
---|
38 | 34 | #define DCSS_LOADNSRX 0x24 |
---|
39 | 35 | #define DCSS_FINDSEGX 0x2c |
---|
.. | .. |
---|
53 | 49 | struct qrange range[6]; |
---|
54 | 50 | }; |
---|
55 | 51 | |
---|
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 | | - |
---|
70 | 52 | struct qin64 { |
---|
71 | 53 | char qopcode; |
---|
72 | 54 | char rsrv1[3]; |
---|
.. | .. |
---|
83 | 65 | char res_name[16]; |
---|
84 | 66 | unsigned long start_addr; |
---|
85 | 67 | unsigned long end; |
---|
86 | | - atomic_t ref_count; |
---|
| 68 | + refcount_t ref_count; |
---|
87 | 69 | int do_nonshared; |
---|
88 | 70 | unsigned int vm_segtype; |
---|
89 | 71 | struct qrange range[6]; |
---|
.. | .. |
---|
95 | 77 | static LIST_HEAD(dcss_list); |
---|
96 | 78 | static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", |
---|
97 | 79 | "EW/EN-MIXED" }; |
---|
98 | | -static int loadshr_scode, loadnsr_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 | | - char *name = kmalloc(8, GFP_KERNEL | GFP_DMA); |
---|
107 | | - unsigned long rx, ry; |
---|
108 | | - int rc; |
---|
109 | | - |
---|
110 | | - if (name == NULL) |
---|
111 | | - return -ENOMEM; |
---|
112 | | - |
---|
113 | | - rx = (unsigned long) name; |
---|
114 | | - ry = DCSS_FINDSEGX; |
---|
115 | | - |
---|
116 | | - strcpy(name, "dummy"); |
---|
117 | | - diag_stat_inc(DIAG_STAT_X064); |
---|
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", "memory"); |
---|
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 | | - segext_scode = DCSS_SEGEXTX; |
---|
135 | | - return 0; |
---|
136 | | - } |
---|
137 | | - /* Diag x'64' new subcodes are not supported, set to old subcodes */ |
---|
138 | | - loadshr_scode = DCSS_LOADNOLY; |
---|
139 | | - loadnsr_scode = DCSS_LOADNSR; |
---|
140 | | - purgeseg_scode = DCSS_PURGESEG; |
---|
141 | | - segext_scode = DCSS_SEGEXT; |
---|
142 | | - return 0; |
---|
143 | | -} |
---|
| 80 | +static int loadshr_scode = DCSS_LOADSHRX; |
---|
| 81 | +static int loadnsr_scode = DCSS_LOADNSRX; |
---|
| 82 | +static int purgeseg_scode = DCSS_PURGESEG; |
---|
| 83 | +static int segext_scode = DCSS_SEGEXTX; |
---|
144 | 84 | |
---|
145 | 85 | /* |
---|
146 | 86 | * Create the 8 bytes, ebcdic VM segment name from |
---|
.. | .. |
---|
196 | 136 | unsigned long rx, ry; |
---|
197 | 137 | int rc; |
---|
198 | 138 | |
---|
199 | | - if (scode_set == 0) { |
---|
200 | | - rc = dcss_set_subcodes(); |
---|
201 | | - if (rc < 0) |
---|
202 | | - return rc; |
---|
203 | | - scode_set = 1; |
---|
204 | | - } |
---|
205 | 139 | rx = (unsigned long) parameter; |
---|
206 | 140 | ry = (unsigned long) *func; |
---|
207 | 141 | |
---|
208 | | - /* 64-bit Diag x'64' new subcode, keep in 64-bit addressing mode */ |
---|
209 | 142 | diag_stat_inc(DIAG_STAT_X064); |
---|
210 | | - if (*func > DCSS_SEGEXT) |
---|
211 | | - asm volatile( |
---|
212 | | - " diag %0,%1,0x64\n" |
---|
213 | | - " ipm %2\n" |
---|
214 | | - " srl %2,28\n" |
---|
215 | | - : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); |
---|
216 | | - /* 31-bit Diag x'64' old subcode, switch to 31-bit addressing mode */ |
---|
217 | | - else |
---|
218 | | - asm volatile( |
---|
219 | | - " sam31\n" |
---|
220 | | - " diag %0,%1,0x64\n" |
---|
221 | | - " sam64\n" |
---|
222 | | - " ipm %2\n" |
---|
223 | | - " srl %2,28\n" |
---|
224 | | - : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); |
---|
| 143 | + asm volatile( |
---|
| 144 | + " diag %0,%1,0x64\n" |
---|
| 145 | + " ipm %2\n" |
---|
| 146 | + " srl %2,28\n" |
---|
| 147 | + : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); |
---|
225 | 148 | *ret1 = rx; |
---|
226 | 149 | *ret2 = ry; |
---|
227 | 150 | return rc; |
---|
.. | .. |
---|
271 | 194 | goto out_free; |
---|
272 | 195 | } |
---|
273 | 196 | |
---|
274 | | - /* Only old format of output area of Diagnose x'64' is supported, |
---|
275 | | - copy data for the new format. */ |
---|
276 | | - if (segext_scode == DCSS_SEGEXT) { |
---|
277 | | - struct qout64_old *qout_old; |
---|
278 | | - qout_old = kzalloc(sizeof(*qout_old), GFP_KERNEL | GFP_DMA); |
---|
279 | | - if (qout_old == NULL) { |
---|
280 | | - rc = -ENOMEM; |
---|
281 | | - goto out_free; |
---|
282 | | - } |
---|
283 | | - memcpy(qout_old, qout, sizeof(struct qout64_old)); |
---|
284 | | - qout->segstart = (unsigned long) qout_old->segstart; |
---|
285 | | - qout->segend = (unsigned long) qout_old->segend; |
---|
286 | | - qout->segcnt = qout_old->segcnt; |
---|
287 | | - qout->segrcnt = qout_old->segrcnt; |
---|
288 | | - |
---|
289 | | - if (qout->segcnt > 6) |
---|
290 | | - qout->segrcnt = 6; |
---|
291 | | - for (i = 0; i < qout->segrcnt; i++) { |
---|
292 | | - qout->range[i].start = |
---|
293 | | - (unsigned long) qout_old->range[i].start; |
---|
294 | | - qout->range[i].end = |
---|
295 | | - (unsigned long) qout_old->range[i].end; |
---|
296 | | - } |
---|
297 | | - kfree(qout_old); |
---|
298 | | - } |
---|
299 | 197 | if (qout->segcnt > 6) { |
---|
300 | 198 | rc = -EOPNOTSUPP; |
---|
301 | 199 | goto out_free; |
---|
.. | .. |
---|
391 | 289 | |
---|
392 | 290 | /* |
---|
393 | 291 | * real segment loading function, called from segment_load |
---|
| 292 | + * Must return either an error code < 0, or the segment type code >= 0 |
---|
394 | 293 | */ |
---|
395 | 294 | static int |
---|
396 | 295 | __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end) |
---|
397 | 296 | { |
---|
398 | 297 | unsigned long start_addr, end_addr, dummy; |
---|
399 | 298 | struct dcss_segment *seg; |
---|
400 | | - int rc, diag_cc; |
---|
| 299 | + int rc, diag_cc, segtype; |
---|
401 | 300 | |
---|
402 | 301 | start_addr = end_addr = 0; |
---|
| 302 | + segtype = -1; |
---|
403 | 303 | seg = kmalloc(sizeof(*seg), GFP_KERNEL | GFP_DMA); |
---|
404 | 304 | if (seg == NULL) { |
---|
405 | 305 | rc = -ENOMEM; |
---|
.. | .. |
---|
410 | 310 | if (rc < 0) |
---|
411 | 311 | goto out_free; |
---|
412 | 312 | |
---|
413 | | - if (loadshr_scode == DCSS_LOADSHRX) { |
---|
414 | | - if (segment_overlaps_others(seg)) { |
---|
415 | | - rc = -EBUSY; |
---|
416 | | - goto out_free; |
---|
417 | | - } |
---|
418 | | - } |
---|
419 | | - |
---|
420 | | - rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1); |
---|
421 | | - |
---|
422 | | - if (rc) |
---|
| 313 | + if (segment_overlaps_others(seg)) { |
---|
| 314 | + rc = -EBUSY; |
---|
423 | 315 | goto out_free; |
---|
| 316 | + } |
---|
424 | 317 | |
---|
425 | 318 | seg->res = kzalloc(sizeof(struct resource), GFP_KERNEL); |
---|
426 | 319 | if (seg->res == NULL) { |
---|
427 | 320 | rc = -ENOMEM; |
---|
428 | | - goto out_shared; |
---|
| 321 | + goto out_free; |
---|
429 | 322 | } |
---|
430 | 323 | seg->res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; |
---|
431 | 324 | seg->res->start = seg->start_addr; |
---|
.. | .. |
---|
435 | 328 | seg->res_name[8] = '\0'; |
---|
436 | 329 | strlcat(seg->res_name, " (DCSS)", sizeof(seg->res_name)); |
---|
437 | 330 | seg->res->name = seg->res_name; |
---|
438 | | - rc = seg->vm_segtype; |
---|
439 | | - if (rc == SEG_TYPE_SC || |
---|
440 | | - ((rc == SEG_TYPE_SR || rc == SEG_TYPE_ER) && !do_nonshared)) |
---|
| 331 | + segtype = seg->vm_segtype; |
---|
| 332 | + if (segtype == SEG_TYPE_SC || |
---|
| 333 | + ((segtype == SEG_TYPE_SR || segtype == SEG_TYPE_ER) && !do_nonshared)) |
---|
441 | 334 | seg->res->flags |= IORESOURCE_READONLY; |
---|
| 335 | + |
---|
| 336 | + /* Check for overlapping resources before adding the mapping. */ |
---|
442 | 337 | if (request_resource(&iomem_resource, seg->res)) { |
---|
443 | 338 | rc = -EBUSY; |
---|
444 | | - kfree(seg->res); |
---|
445 | | - goto out_shared; |
---|
| 339 | + goto out_free_resource; |
---|
446 | 340 | } |
---|
| 341 | + |
---|
| 342 | + rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1); |
---|
| 343 | + if (rc) |
---|
| 344 | + goto out_resource; |
---|
447 | 345 | |
---|
448 | 346 | if (do_nonshared) |
---|
449 | 347 | diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, |
---|
.. | .. |
---|
455 | 353 | dcss_diag(&purgeseg_scode, seg->dcss_name, |
---|
456 | 354 | &dummy, &dummy); |
---|
457 | 355 | rc = diag_cc; |
---|
458 | | - goto out_resource; |
---|
| 356 | + goto out_mapping; |
---|
459 | 357 | } |
---|
460 | 358 | if (diag_cc > 1) { |
---|
461 | 359 | pr_warn("Loading DCSS %s failed with rc=%ld\n", name, end_addr); |
---|
462 | 360 | rc = dcss_diag_translate_rc(end_addr); |
---|
463 | 361 | dcss_diag(&purgeseg_scode, seg->dcss_name, |
---|
464 | 362 | &dummy, &dummy); |
---|
465 | | - goto out_resource; |
---|
| 363 | + goto out_mapping; |
---|
466 | 364 | } |
---|
467 | 365 | seg->start_addr = start_addr; |
---|
468 | 366 | seg->end = end_addr; |
---|
469 | 367 | seg->do_nonshared = do_nonshared; |
---|
470 | | - atomic_set(&seg->ref_count, 1); |
---|
| 368 | + refcount_set(&seg->ref_count, 1); |
---|
471 | 369 | list_add(&seg->list, &dcss_list); |
---|
472 | 370 | *addr = seg->start_addr; |
---|
473 | 371 | *end = seg->end; |
---|
474 | 372 | if (do_nonshared) |
---|
475 | | - pr_info("DCSS %s of range %p to %p and type %s loaded as " |
---|
| 373 | + pr_info("DCSS %s of range %px to %px and type %s loaded as " |
---|
476 | 374 | "exclusive-writable\n", name, (void*) seg->start_addr, |
---|
477 | 375 | (void*) seg->end, segtype_string[seg->vm_segtype]); |
---|
478 | 376 | else { |
---|
479 | | - pr_info("DCSS %s of range %p to %p and type %s loaded in " |
---|
| 377 | + pr_info("DCSS %s of range %px to %px and type %s loaded in " |
---|
480 | 378 | "shared access mode\n", name, (void*) seg->start_addr, |
---|
481 | 379 | (void*) seg->end, segtype_string[seg->vm_segtype]); |
---|
482 | 380 | } |
---|
483 | 381 | goto out; |
---|
| 382 | + out_mapping: |
---|
| 383 | + vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); |
---|
484 | 384 | out_resource: |
---|
485 | 385 | release_resource(seg->res); |
---|
| 386 | + out_free_resource: |
---|
486 | 387 | kfree(seg->res); |
---|
487 | | - out_shared: |
---|
488 | | - vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); |
---|
489 | 388 | out_free: |
---|
490 | 389 | kfree(seg); |
---|
491 | 390 | out: |
---|
492 | | - return rc; |
---|
| 391 | + return rc < 0 ? rc : segtype; |
---|
493 | 392 | } |
---|
494 | 393 | |
---|
495 | 394 | /* |
---|
.. | .. |
---|
504 | 403 | * -EIO : could not perform query or load diagnose |
---|
505 | 404 | * -ENOENT : no such segment |
---|
506 | 405 | * -EOPNOTSUPP: multi-part segment cannot be used with linux |
---|
507 | | - * -ENOSPC : segment cannot be used (overlaps with storage) |
---|
508 | | - * -EBUSY : segment can temporarily not be used (overlaps with dcss) |
---|
| 406 | + * -EBUSY : segment cannot be used (overlaps with dcss or storage) |
---|
509 | 407 | * -ERANGE : segment cannot be used (exceeds kernel mapping range) |
---|
510 | 408 | * -EPERM : segment is currently loaded with incompatible permissions |
---|
511 | 409 | * -ENOMEM : out of memory |
---|
.. | .. |
---|
527 | 425 | rc = __segment_load (name, do_nonshared, addr, end); |
---|
528 | 426 | else { |
---|
529 | 427 | if (do_nonshared == seg->do_nonshared) { |
---|
530 | | - atomic_inc(&seg->ref_count); |
---|
| 428 | + refcount_inc(&seg->ref_count); |
---|
531 | 429 | *addr = seg->start_addr; |
---|
532 | 430 | *end = seg->end; |
---|
533 | 431 | rc = seg->vm_segtype; |
---|
.. | .. |
---|
573 | 471 | rc = 0; |
---|
574 | 472 | goto out_unlock; |
---|
575 | 473 | } |
---|
576 | | - if (atomic_read (&seg->ref_count) != 1) { |
---|
| 474 | + if (refcount_read(&seg->ref_count) != 1) { |
---|
577 | 475 | pr_warn("DCSS %s is in use and cannot be reloaded\n", name); |
---|
578 | 476 | rc = -EAGAIN; |
---|
579 | 477 | goto out_unlock; |
---|
.. | .. |
---|
649 | 547 | pr_err("Unloading unknown DCSS %s failed\n", name); |
---|
650 | 548 | goto out_unlock; |
---|
651 | 549 | } |
---|
652 | | - if (atomic_dec_return(&seg->ref_count) != 0) |
---|
| 550 | + if (!refcount_dec_and_test(&seg->ref_count)) |
---|
653 | 551 | goto out_unlock; |
---|
654 | 552 | release_resource(seg->res); |
---|
655 | 553 | kfree(seg->res); |
---|
.. | .. |
---|
729 | 627 | case -EOPNOTSUPP: |
---|
730 | 628 | pr_err("DCSS %s has multiple page ranges and cannot be " |
---|
731 | 629 | "loaded or queried\n", seg_name); |
---|
732 | | - break; |
---|
733 | | - case -ENOSPC: |
---|
734 | | - pr_err("DCSS %s overlaps with used storage and cannot " |
---|
735 | | - "be loaded\n", seg_name); |
---|
736 | 630 | break; |
---|
737 | 631 | case -EBUSY: |
---|
738 | 632 | pr_err("%s needs used memory resources and cannot be " |
---|