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