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