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/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
  41struct qrange {
  42        unsigned long  start; /* last byte type */
  43        unsigned long  end;   /* last byte reserved */
  44};
  45
  46struct 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
  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#endif
  69
  70struct 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
  80struct 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;
  86        atomic_t ref_count;
  87        int do_nonshared;
  88        unsigned int vm_segtype;
  89        struct qrange range[6];
  90        int segcnt;
  91        struct resource *res;
  92};
  93
  94static DEFINE_MUTEX(dcss_lock);
  95static LIST_HEAD(dcss_list);
  96static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
  97                                        "EW/EN-MIXED" };
  98static int loadshr_scode, loadnsr_scode, findseg_scode;
  99static int segext_scode, purgeseg_scode;
 100static int scode_set;
 101
 102/* set correct Diag x'64' subcodes. */
 103static int
 104dcss_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 */
 152static void
 153dcss_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 */
 172static struct dcss_segment *
 173segment_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 */
 195static inline int
 196dcss_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
 240static inline int
 241dcss_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 */
 251static int
 252query_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 */
 364int
 365segment_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 */
 384static int
 385segment_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 */
 407static 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 */
 527int
 528segment_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 */
 569int
 570segment_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 */
 651void
 652segment_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);
 674out_unlock:
 675        mutex_unlock(&dcss_lock);
 676}
 677
 678/*
 679 * save segment content permanently
 680 */
 681void
 682segment_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        }
 721out:
 722        mutex_unlock(&dcss_lock);
 723}
 724
 725/*
 726 * print appropriate error message for segment_load()/segment_type()
 727 * return code
 728 */
 729void 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
 772EXPORT_SYMBOL(segment_load);
 773EXPORT_SYMBOL(segment_unload);
 774EXPORT_SYMBOL(segment_save);
 775EXPORT_SYMBOL(segment_type);
 776EXPORT_SYMBOL(segment_modify_shared);
 777EXPORT_SYMBOL(segment_warning);
 778