linux/arch/s390/mm/extmem.c
<<
>>
Prefs
   1/*
   2 * File...........: arch/s390/mm/extmem.c
   3 * Author(s)......: Carsten Otte <cotte@de.ibm.com>
   4 *                  Rob M van der Heij <rvdheij@nl.ibm.com>
   5 *                  Steven Shultz <shultzss@us.ibm.com>
   6 * Bugreports.to..: <Linux390@de.ibm.com>
   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
  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
  55#ifdef CONFIG_64BIT
  56struct 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' */
  62struct qout64_old {
  63        int segstart;
  64        int segend;
  65        int segcnt;
  66        int segrcnt;
  67        struct qrange_old range[6];
  68};
  69#endif
  70
  71struct 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
  81struct 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
  95static DEFINE_MUTEX(dcss_lock);
  96static LIST_HEAD(dcss_list);
  97static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
  98                                        "EW/EN-MIXED" };
  99static int loadshr_scode, loadnsr_scode, findseg_scode;
 100static int segext_scode, purgeseg_scode;
 101static int scode_set;
 102
 103/* set correct Diag x'64' subcodes. */
 104static int
 105dcss_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 */
 153static void
 154dcss_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 */
 173static struct dcss_segment *
 174segment_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 */
 196static inline int
 197dcss_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
 241static inline int
 242dcss_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 */
 252static int
 253query_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 */
 365int
 366segment_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 */
 385static int
 386segment_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 */
 408static 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 */
 528int
 529segment_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 */
 570int
 571segment_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 */
 652void
 653segment_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);
 675out_unlock:
 676        mutex_unlock(&dcss_lock);
 677}
 678
 679/*
 680 * save segment content permanently
 681 */
 682void
 683segment_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        }
 722out:
 723        mutex_unlock(&dcss_lock);
 724}
 725
 726/*
 727 * print appropriate error message for segment_load()/segment_type()
 728 * return code
 729 */
 730void 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
 773EXPORT_SYMBOL(segment_load);
 774EXPORT_SYMBOL(segment_unload);
 775EXPORT_SYMBOL(segment_save);
 776EXPORT_SYMBOL(segment_type);
 777EXPORT_SYMBOL(segment_modify_shared);
 778EXPORT_SYMBOL(segment_warning);
 779