X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fs390%2Fmm%2Fextmem.c;h=648deed17e25f8c2e39e97f01141bc0e09d496a2;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=b0b2714eac2880b60321837a17afd6053d644f00;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/arch/s390/mm/extmem.c b/arch/s390/mm/extmem.c index b0b2714ea..648deed17 100644 --- a/arch/s390/mm/extmem.c +++ b/arch/s390/mm/extmem.c @@ -1,11 +1,10 @@ /* - * File...........: arch/s390/mm/dcss.c - * Author(s)......: Steven Shultz - * Carsten Otte + * File...........: arch/s390/mm/extmem.c + * Author(s)......: Carsten Otte + * Rob M van der Heij + * Steven Shultz * Bugreports.to..: - * thanks to Rob M van der Heij - * - he wrote the diag64 function - * (C) IBM Corporation 2002 + * (C) IBM Corporation 2002-2004 */ #include @@ -41,20 +40,48 @@ #define DCSS_FINDSEG 0x0c #define DCSS_LOADNOLY 0x10 #define DCSS_SEGEXT 0x18 -#define DCSS_QACTV 0x0c +#define DCSS_FINDSEGA 0x0c + +struct qrange { + unsigned int start; // 3byte start address, 1 byte type + unsigned int end; // 3byte end address, 1 byte reserved +}; + +struct qout64 { + int segstart; + int segend; + int segcnt; + int segrcnt; + struct qrange range[6]; +}; + +struct qin64 { + char qopcode; + char rsrv1[3]; + char qrcode; + char rsrv2[3]; + char qname[8]; + unsigned int qoutptr; + short int qoutlen; +}; struct dcss_segment { - struct list_head list; - char dcss_name[8]; - unsigned long start_addr; - unsigned long end; - atomic_t ref_count; - int dcss_attr; - int shared_attr; + struct list_head list; + char dcss_name[8]; + unsigned long start_addr; + unsigned long end; + atomic_t ref_count; + int do_nonshared; + unsigned int vm_segtype; + struct qrange range[6]; + int segcnt; }; -static spinlock_t dcss_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(dcss_lock); static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list); +static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", + "EW/EN-MIXED" }; + extern struct { unsigned long addr, size, type; } memory_chunk[MEMORY_CHUNKS]; @@ -63,20 +90,46 @@ extern struct { * Create the 8 bytes, ebcdic VM segment name from * an ascii name. */ -static void inline dcss_mkname(char *name, char *dcss_name) +static void inline +dcss_mkname(char *name, char *dcss_name) +{ + int i; + + for (i = 0; i < 8; i++) { + if (name[i] == '\0') + break; + dcss_name[i] = toupper(name[i]); + }; + for (; i < 8; i++) + dcss_name[i] = ' '; + ASCEBC(dcss_name, 8); +} + + +/* + * search all segments in dcss_list, and return the one + * namend *name. If not found, return NULL. + */ +static struct dcss_segment * +segment_by_name (char *name) { - int i; - - for (i = 0; i <= 8; i++) { - if (name[i] == '\0') - break; - dcss_name[i] = toupper(name[i]); - }; - for (; i <= 8; i++) - dcss_name[i] = ' '; - ASCEBC(dcss_name, 8); + char dcss_name[9]; + struct list_head *l; + struct dcss_segment *tmp, *retval = NULL; + + assert_spin_locked(&dcss_lock); + dcss_mkname (name, dcss_name); + list_for_each (l, &dcss_list) { + tmp = list_entry (l, struct dcss_segment, list); + if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) { + retval = tmp; + break; + } + } + return retval; } + /* * Perform a function on a dcss segment. */ @@ -84,337 +137,378 @@ static inline int dcss_diag (__u8 func, void *parameter, unsigned long *ret1, unsigned long *ret2) { - unsigned long rx, ry; - int rc; + unsigned long rx, ry; + int rc; - rx = (unsigned long) parameter; - ry = (unsigned long) func; - __asm__ __volatile__( + rx = (unsigned long) parameter; + ry = (unsigned long) func; + __asm__ __volatile__( #ifdef CONFIG_ARCH_S390X - " sam31\n" // switch to 31 bit - " diag %0,%1,0x64\n" - " sam64\n" // switch back to 64 bit + " sam31\n" // switch to 31 bit + " diag %0,%1,0x64\n" + " sam64\n" // switch back to 64 bit #else - " diag %0,%1,0x64\n" + " diag %0,%1,0x64\n" #endif - " ipm %2\n" - " srl %2,28\n" - : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" ); - *ret1 = rx; - *ret2 = ry; - return rc; + " ipm %2\n" + " srl %2,28\n" + : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" ); + *ret1 = rx; + *ret2 = ry; + return rc; +} + +static inline int +dcss_diag_translate_rc (int vm_rc) { + if (vm_rc == 44) + return -ENOENT; + return -EIO; +} + + +/* do a diag to get info about a segment. + * fills start_address, end and vm_segtype fields + */ +static int +query_segment_type (struct dcss_segment *seg) +{ + struct qin64 *qin = kmalloc (sizeof(struct qin64), GFP_DMA); + struct qout64 *qout = kmalloc (sizeof(struct qout64), GFP_DMA); + + int diag_cc, rc, i; + unsigned long dummy, vmrc; + + if ((qin == NULL) || (qout == NULL)) { + rc = -ENOMEM; + goto out_free; + } + + /* initialize diag input parameters */ + qin->qopcode = DCSS_FINDSEGA; + qin->qoutptr = (unsigned long) qout; + qin->qoutlen = sizeof(struct qout64); + memcpy (qin->qname, seg->dcss_name, 8); + + diag_cc = dcss_diag (DCSS_SEGEXT, qin, &dummy, &vmrc); + + if (diag_cc > 1) { + rc = dcss_diag_translate_rc (vmrc); + goto out_free; + } + + if (qout->segcnt > 6) { + rc = -ENOTSUPP; + goto out_free; + } + + if (qout->segcnt == 1) { + seg->vm_segtype = qout->range[0].start & 0xff; + } else { + /* multi-part segment. only one type supported here: + - all parts are contiguous + - all parts are either EW or EN type + - maximum 6 parts allowed */ + unsigned long start = qout->segstart >> PAGE_SHIFT; + for (i=0; isegcnt; i++) { + if (((qout->range[i].start & 0xff) != SEG_TYPE_EW) && + ((qout->range[i].start & 0xff) != SEG_TYPE_EN)) { + rc = -ENOTSUPP; + goto out_free; + } + if (start != qout->range[i].start >> PAGE_SHIFT) { + rc = -ENOTSUPP; + goto out_free; + } + start = (qout->range[i].end >> PAGE_SHIFT) + 1; + } + seg->vm_segtype = SEG_TYPE_EWEN; + } + + /* analyze diag output and update seg */ + seg->start_addr = qout->segstart; + seg->end = qout->segend; + + memcpy (seg->range, qout->range, 6*sizeof(struct qrange)); + seg->segcnt = qout->segcnt; + + rc = 0; + + out_free: + if (qin) kfree(qin); + if (qout) kfree(qout); + return rc; +} + +/* + * check if the given segment collides with guest storage. + * returns 1 if this is the case, 0 if no collision was found + */ +static int +segment_overlaps_storage(struct dcss_segment *seg) +{ + int i; + + for (i=0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { + if (memory_chunk[i].type != 0) + continue; + if ((memory_chunk[i].addr >> 20) > (seg->end >> 20)) + continue; + if (((memory_chunk[i].addr + memory_chunk[i].size - 1) >> 20) + < (seg->start_addr >> 20)) + continue; + return 1; + } + return 0; } +/* + * check if segment collides with other segments that are currently loaded + * returns 1 if this is the case, 0 if no collision was found + */ +static int +segment_overlaps_others (struct dcss_segment *seg) +{ + struct list_head *l; + struct dcss_segment *tmp; + + assert_spin_locked(&dcss_lock); + list_for_each(l, &dcss_list) { + tmp = list_entry(l, struct dcss_segment, list); + if ((tmp->start_addr >> 20) > (seg->end >> 20)) + continue; + if ((tmp->end >> 20) < (seg->start_addr >> 20)) + continue; + if (seg == tmp) + continue; + return 1; + } + return 0; +} -/* use to issue "extended" dcss query */ +/* + * check if segment exceeds the kernel mapping range (detected or set via mem=) + * returns 1 if this is the case, 0 if segment fits into the range + */ static inline int -dcss_diag_query(char *name, int *rwattr, int *shattr, unsigned long *segstart, unsigned long *segend) +segment_exceeds_range (struct dcss_segment *seg) { - int i,j,rc; - unsigned long rx, ry; - - typedef struct segentry { - char thisseg[8]; - } segentry; - - struct qout64 { - int segstart; - int segend; - int segcnt; - int segrcnt; - segentry segout[6]; - }; - - struct qin64 { - char qopcode; - char rsrv1[3]; - char qrcode; - char rsrv2[3]; - char qname[8]; - unsigned int qoutptr; - short int qoutlen; - }; - - - struct qin64 *qinarea; - struct qout64 *qoutarea; - - qinarea = (struct qin64*) get_zeroed_page (GFP_DMA); - if (!qinarea) { - rc =-ENOMEM; - goto out; - } - qoutarea = (struct qout64*) get_zeroed_page (GFP_DMA); - if (!qoutarea) { - rc = -ENOMEM; - free_page ((unsigned long) qinarea); - goto out; - } - memset (qinarea,0,PAGE_SIZE); - memset (qoutarea,0,PAGE_SIZE); - - qinarea->qopcode = DCSS_QACTV; /* do a query for active - segments */ - qinarea->qoutptr = (unsigned long) qoutarea; - qinarea->qoutlen = sizeof(struct qout64); - - /* Move segment name into double word aligned - field and pad with blanks to 8 long. - */ - - for (i = j = 0 ; i < 8; i++) { - qinarea->qname[i] = (name[j] == '\0') ? ' ' : name[j++]; - } - - /* name already in EBCDIC */ - /* ASCEBC ((void *)&qinarea.qname, 8); */ - - /* set the assembler variables */ - rx = (unsigned long) qinarea; - ry = DCSS_SEGEXT; /* this is extended function */ - - /* issue diagnose x'64' */ - __asm__ __volatile__( -#ifdef CONFIG_ARCH_S390X - " sam31\n" // switch to 31 bit - " diag %0,%1,0x64\n" - " sam64\n" // switch back to 64 bit -#else - " diag %0,%1,0x64\n" -#endif - " ipm %2\n" - " srl %2,28\n" - : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" ); - - /* parse the query output area */ - *segstart=qoutarea->segstart; - *segend=qoutarea->segend; - - if (rc > 1) - { - *rwattr = 2; - *shattr = 2; - rc = 0; - goto free; - } - - if (qoutarea->segcnt > 6) - { - *rwattr = 3; - *shattr = 3; - rc = 0; - goto free; - } - - *rwattr = 1; - *shattr = 1; - - for (i=0; i < qoutarea->segrcnt; i++) { - if (qoutarea->segout[i].thisseg[3] == 2 || - qoutarea->segout[i].thisseg[3] == 3 || - qoutarea->segout[i].thisseg[3] == 6 ) - *rwattr = 0; - if (qoutarea->segout[i].thisseg[3] == 1 || - qoutarea->segout[i].thisseg[3] == 3 || - qoutarea->segout[i].thisseg[3] == 5 ) - *shattr = 0; - } /* end of for statement */ - rc = 0; - free: - free_page ((unsigned long) qoutarea); - free_page ((unsigned long) qinarea); + int seg_last_pfn = (seg->end) >> PAGE_SHIFT; + if (seg_last_pfn > max_pfn) + return 1; + return 0; +} + +/* + * get info about a segment + * possible return values: + * -ENOSYS : we are not running on VM + * -EIO : could not perform query diagnose + * -ENOENT : no such segment + * -ENOTSUPP: multi-part segment cannot be used with linux + * -ENOSPC : segment cannot be used (overlaps with storage) + * -ENOMEM : out of memory + * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h + */ +int +segment_type (char* name) +{ + int rc; + struct dcss_segment seg; + + if (!MACHINE_IS_VM) + return -ENOSYS; + + dcss_mkname(name, seg.dcss_name); + rc = query_segment_type (&seg); + if (rc < 0) + return rc; + return seg.vm_segtype; +} + +/* + * real segment loading function, called from segment_load + */ +static int +__segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long *end) +{ + struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment), + GFP_DMA); + int dcss_command, rc, diag_cc; + + if (seg == NULL) { + rc = -ENOMEM; + goto out; + } + dcss_mkname (name, seg->dcss_name); + rc = query_segment_type (seg); + if (rc < 0) + goto out_free; + if (segment_exceeds_range(seg)) { + PRINT_WARN ("segment_load: not loading segment %s - exceeds" + " kernel mapping range\n",name); + rc = -ERANGE; + goto out_free; + } + if (segment_overlaps_storage(seg)) { + PRINT_WARN ("segment_load: not loading segment %s - overlaps" + " storage\n",name); + rc = -ENOSPC; + goto out_free; + } + if (segment_overlaps_others(seg)) { + PRINT_WARN ("segment_load: not loading segment %s - overlaps" + " other segments\n",name); + rc = -EBUSY; + goto out_free; + } + if (do_nonshared) + dcss_command = DCSS_LOADNSR; + else + dcss_command = DCSS_LOADNOLY; + + diag_cc = dcss_diag(dcss_command, seg->dcss_name, + &seg->start_addr, &seg->end); + if (diag_cc > 1) { + PRINT_WARN ("segment_load: could not load segment %s - " + "diag returned error (%ld)\n",name,seg->end); + rc = dcss_diag_translate_rc (seg->end); + dcss_diag(DCSS_PURGESEG, seg->dcss_name, + &seg->start_addr, &seg->end); + goto out_free; + } + seg->do_nonshared = do_nonshared; + atomic_set(&seg->ref_count, 1); + list_add(&seg->list, &dcss_list); + rc = seg->vm_segtype; + *addr = seg->start_addr; + *end = seg->end; + if (do_nonshared) + PRINT_INFO ("segment_load: loaded segment %s range %p .. %p " + "type %s in non-shared mode\n", name, + (void*)seg->start_addr, (void*)seg->end, + segtype_string[seg->vm_segtype]); + else + PRINT_INFO ("segment_load: loaded segment %s range %p .. %p " + "type %s in shared mode\n", name, + (void*)seg->start_addr, (void*)seg->end, + segtype_string[seg->vm_segtype]); + goto out; + out_free: + kfree (seg); out: - return rc; + return rc; } /* - * Load a DCSS segment via the diag 0x64. + * this function loads a DCSS segment + * name : name of the DCSS + * do_nonshared : 0 indicates that the dcss should be shared with other linux images + * 1 indicates that the dcss should be exclusive for this linux image + * addr : will be filled with start address of the segment + * end : will be filled with end address of the segment + * return values: + * -ENOSYS : we are not running on VM + * -EIO : could not perform query or load diagnose + * -ENOENT : no such segment + * -ENOTSUPP: multi-part segment cannot be used with linux + * -ENOSPC : segment cannot be used (overlaps with storage) + * -EBUSY : segment can temporarily not be used (overlaps with dcss) + * -ERANGE : segment cannot be used (exceeds kernel mapping range) + * -EPERM : segment is currently loaded with incompatible permissions + * -ENOMEM : out of memory + * 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h */ -int segment_load(char *name, int segtype, unsigned long *addr, - unsigned long *end) +int +segment_load (char *name, int do_nonshared, unsigned long *addr, + unsigned long *end) { - char dcss_name[8]; - struct list_head *l; - struct dcss_segment *seg, *tmp; - unsigned long dummy; - unsigned long segstart, segend; - int rc = 0,i; - int rwattr, shattr; - - if (!MACHINE_IS_VM) - return -ENOSYS; - dcss_mkname(name, dcss_name); - /* search for the dcss in list of currently loaded segments */ - spin_lock(&dcss_lock); - seg = NULL; - list_for_each(l, &dcss_list) { - tmp = list_entry(l, struct dcss_segment, list); - if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) { - seg = tmp; - break; - } - } - - if (seg == NULL) { - /* find out the attributes of this - shared segment */ - dcss_diag_query(dcss_name, &rwattr, &shattr, &segstart, &segend); - /* does segment collide with main memory? */ - for (i=0; i < MEMORY_CHUNKS; i++) { - if (memory_chunk[i].type != 0) - continue; - if (memory_chunk[i].addr > segend) - continue; - if (memory_chunk[i].addr + memory_chunk[i].size <= segstart) - continue; - spin_unlock(&dcss_lock); - return -ENOENT; - } - /* or does it collide with other (loaded) segments? */ - list_for_each(l, &dcss_list) { - tmp = list_entry(l, struct dcss_segment, list); - if ((segstart <= tmp->end && segstart >= tmp->start_addr) || - (segend <= tmp->end && segend >= tmp->start_addr) || - (segstart <= tmp->start_addr && segend >= tmp->end)) { - PRINT_ERR("Segment Overlap!\n"); - spin_unlock(&dcss_lock); - return -ENOENT; - } - } - - /* do case statement on segtype */ - /* if asking for shared ro, - shared rw works */ - /* if asking for exclusive ro, - exclusive rw works */ - - switch(segtype) { - case SEGMENT_SHARED_RO: - if (shattr > 1 || rwattr > 1) { - spin_unlock(&dcss_lock); - return -ENOENT; - } else { - if (shattr == 0 && rwattr == 0) - rc = SEGMENT_EXCLUSIVE_RO; - if (shattr == 0 && rwattr == 1) - rc = SEGMENT_EXCLUSIVE_RW; - if (shattr == 1 && rwattr == 0) - rc = SEGMENT_SHARED_RO; - if (shattr == 1 && rwattr == 1) - rc = SEGMENT_SHARED_RW; - } - break; - case SEGMENT_SHARED_RW: - if (shattr > 1 || rwattr != 1) { - spin_unlock(&dcss_lock); - return -ENOENT; - } else { - if (shattr == 0) - rc = SEGMENT_EXCLUSIVE_RW; - if (shattr == 1) - rc = SEGMENT_SHARED_RW; - } - break; - - case SEGMENT_EXCLUSIVE_RO: - if (shattr > 0 || rwattr > 1) { - spin_unlock(&dcss_lock); - return -ENOENT; - } else { - if (rwattr == 0) - rc = SEGMENT_EXCLUSIVE_RO; - if (rwattr == 1) - rc = SEGMENT_EXCLUSIVE_RW; - } - break; - - case SEGMENT_EXCLUSIVE_RW: -/* if (shattr != 0 || rwattr != 1) { - spin_unlock(&dcss_lock); - return -ENOENT; - } else { -*/ - rc = SEGMENT_EXCLUSIVE_RW; -// } - break; - - default: - spin_unlock(&dcss_lock); - return -ENOENT; - } /* end switch */ - - seg = kmalloc(sizeof(struct dcss_segment), GFP_DMA); - if (seg != NULL) { - memcpy(seg->dcss_name, dcss_name, 8); - if (rc == SEGMENT_EXCLUSIVE_RW) { - if (dcss_diag(DCSS_LOADNSR, seg->dcss_name, - &seg->start_addr, &seg->end) == 0) { - if (seg->end < max_low_pfn*PAGE_SIZE ) { - atomic_set(&seg->ref_count, 1); - list_add(&seg->list, &dcss_list); - *addr = seg->start_addr; - *end = seg->end; - seg->dcss_attr = rc; - if (shattr == 1 && rwattr == 1) - seg->shared_attr = SEGMENT_SHARED_RW; - else if (shattr == 1 && rwattr == 0) - seg->shared_attr = SEGMENT_SHARED_RO; - else - seg->shared_attr = SEGMENT_EXCLUSIVE_RW; - } else { - dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); - kfree (seg); - rc = -ENOENT; - } - } else { - kfree(seg); - rc = -ENOENT; - } - goto out; - } - if (dcss_diag(DCSS_LOADNOLY, seg->dcss_name, - &seg->start_addr, &seg->end) == 0) { - if (seg->end < max_low_pfn*PAGE_SIZE ) { - atomic_set(&seg->ref_count, 1); - list_add(&seg->list, &dcss_list); - *addr = seg->start_addr; - *end = seg->end; - seg->dcss_attr = rc; - seg->shared_attr = rc; - } else { - dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); - kfree (seg); - rc = -ENOENT; - } - } else { - kfree(seg); - rc = -ENOENT; - } - } else rc = -ENOMEM; - } else { - /* found */ - if ((segtype == SEGMENT_EXCLUSIVE_RW) && (seg->dcss_attr != SEGMENT_EXCLUSIVE_RW)) { - PRINT_ERR("Segment already loaded in other mode than EXCLUSIVE_RW!\n"); - rc = -EPERM; - goto out; - /* reload segment in exclusive mode */ -/* dcss_diag(DCSS_LOADNSR, seg->dcss_name, - &seg->start_addr, &seg->end); - seg->dcss_attr = SEGMENT_EXCLUSIVE_RW;*/ + struct dcss_segment *seg; + int rc; + + if (!MACHINE_IS_VM) + return -ENOSYS; + + spin_lock (&dcss_lock); + seg = segment_by_name (name); + if (seg == NULL) + rc = __segment_load (name, do_nonshared, addr, end); + else { + if (do_nonshared == seg->do_nonshared) { + atomic_inc(&seg->ref_count); + *addr = seg->start_addr; + *end = seg->end; + rc = seg->vm_segtype; + } else { + *addr = *end = 0; + rc = -EPERM; } - if ((segtype != SEGMENT_EXCLUSIVE_RW) && (seg->dcss_attr == SEGMENT_EXCLUSIVE_RW)) { - PRINT_ERR("Segment already loaded in EXCLUSIVE_RW mode!\n"); - rc = -EPERM; - goto out; - } - atomic_inc(&seg->ref_count); - *addr = seg->start_addr; - *end = seg->end; - rc = seg->dcss_attr; - } -out: - spin_unlock(&dcss_lock); - return rc; + } + spin_unlock (&dcss_lock); + return rc; +} + +/* + * this function modifies the shared state of a DCSS segment. note that + * name : name of the DCSS + * do_nonshared : 0 indicates that the dcss should be shared with other linux images + * 1 indicates that the dcss should be exclusive for this linux image + * return values: + * -EIO : could not perform load diagnose (segment gone!) + * -ENOENT : no such segment (segment gone!) + * -EAGAIN : segment is in use by other exploiters, try later + * -EINVAL : no segment with the given name is currently loaded - name invalid + * 0 : operation succeeded + */ +int +segment_modify_shared (char *name, int do_nonshared) +{ + struct dcss_segment *seg; + unsigned long dummy; + int dcss_command, rc, diag_cc; + + spin_lock (&dcss_lock); + seg = segment_by_name (name); + if (seg == NULL) { + rc = -EINVAL; + goto out_unlock; + } + if (do_nonshared == seg->do_nonshared) { + PRINT_INFO ("segment_modify_shared: not reloading segment %s" + " - already in requested mode\n",name); + rc = 0; + goto out_unlock; + } + if (atomic_read (&seg->ref_count) != 1) { + PRINT_WARN ("segment_modify_shared: not reloading segment %s - " + "segment is in use by other driver(s)\n",name); + rc = -EAGAIN; + goto out_unlock; + } + dcss_diag(DCSS_PURGESEG, seg->dcss_name, + &dummy, &dummy); + if (do_nonshared) + dcss_command = DCSS_LOADNSR; + else + dcss_command = DCSS_LOADNOLY; + diag_cc = dcss_diag(dcss_command, seg->dcss_name, + &seg->start_addr, &seg->end); + if (diag_cc > 1) { + PRINT_WARN ("segment_modify_shared: could not reload segment %s" + " - diag returned error (%ld)\n",name,seg->end); + rc = dcss_diag_translate_rc (seg->end); + goto out_del; + } + seg->do_nonshared = do_nonshared; + rc = 0; + goto out_unlock; + out_del: + list_del(&seg->list); + dcss_diag(DCSS_PURGESEG, seg->dcss_name, + &dummy, &dummy); + kfree (seg); + out_unlock: + spin_unlock(&dcss_lock); + return rc; } /* @@ -422,84 +516,73 @@ out: * it from the address space if nobody is using it * any longer. */ -void segment_unload(char *name) +void +segment_unload(char *name) { - char dcss_name[8]; - unsigned long dummy; - struct list_head *l,*l_tmp; - struct dcss_segment *seg; - - if (!MACHINE_IS_VM) - return; - dcss_mkname(name, dcss_name); - spin_lock(&dcss_lock); - list_for_each_safe(l, l_tmp, &dcss_list) { - seg = list_entry(l, struct dcss_segment, list); - if (memcmp(seg->dcss_name, dcss_name, 8) == 0) { - if (atomic_dec_return(&seg->ref_count) == 0) { - /* Last user of the segment is - gone. */ - list_del(&seg->list); - dcss_diag(DCSS_PURGESEG, seg->dcss_name, - &dummy, &dummy); - kfree(seg); - } - break; - } - } - spin_unlock(&dcss_lock); + unsigned long dummy; + struct dcss_segment *seg; + + if (!MACHINE_IS_VM) + return; + + spin_lock(&dcss_lock); + seg = segment_by_name (name); + if (seg == NULL) { + PRINT_ERR ("could not find segment %s in segment_unload, " + "please report to linux390@de.ibm.com\n",name); + goto out_unlock; + } + if (atomic_dec_return(&seg->ref_count) == 0) { + list_del(&seg->list); + dcss_diag(DCSS_PURGESEG, seg->dcss_name, + &dummy, &dummy); + kfree(seg); + } +out_unlock: + spin_unlock(&dcss_lock); } /* - * Replace an existing DCSS segment, so that machines - * that load it anew will see the new version. + * save segment content permanently */ -void segment_replace(char *name) +void +segment_save(char *name) { - char dcss_name[8]; - struct list_head *l; - struct dcss_segment *seg; - int mybeg = 0; - int myend = 0; - char mybuff1[80]; - char mybuff2[80]; - - if (!MACHINE_IS_VM) - return; - dcss_mkname(name, dcss_name); - - memset (mybuff1, 0, sizeof(mybuff1)); - memset (mybuff2, 0, sizeof(mybuff2)); - - spin_lock(&dcss_lock); - list_for_each(l, &dcss_list) { - seg = list_entry(l, struct dcss_segment, list); - if (memcmp(seg->dcss_name, dcss_name, 8) == 0) { - mybeg = seg->start_addr >> 12; - myend = (seg->end) >> 12; - if (seg->shared_attr == SEGMENT_EXCLUSIVE_RW) - sprintf(mybuff1, "DEFSEG %s %X-%X EW", - name, mybeg, myend); - if (seg->shared_attr == SEGMENT_EXCLUSIVE_RO) - sprintf(mybuff1, "DEFSEG %s %X-%X RO", - name, mybeg, myend); - if (seg->shared_attr == SEGMENT_SHARED_RW) - sprintf(mybuff1, "DEFSEG %s %X-%X SW", - name, mybeg, myend); - if (seg->shared_attr == SEGMENT_SHARED_RO) - sprintf(mybuff1, "DEFSEG %s %X-%X SR", - name, mybeg, myend); - spin_unlock(&dcss_lock); - sprintf(mybuff2, "SAVESEG %s", name); - cpcmd(mybuff1, NULL, 80); - cpcmd(mybuff2, NULL, 80); - break; - } - - } - if (myend == 0) spin_unlock(&dcss_lock); + struct dcss_segment *seg; + int startpfn = 0; + int endpfn = 0; + char cmd1[160]; + char cmd2[80]; + int i; + + if (!MACHINE_IS_VM) + return; + + spin_lock(&dcss_lock); + seg = segment_by_name (name); + + if (seg == NULL) { + PRINT_ERR ("could not find segment %s in segment_save, please report to linux390@de.ibm.com\n",name); + return; + } + + startpfn = seg->start_addr >> PAGE_SHIFT; + endpfn = (seg->end) >> PAGE_SHIFT; + sprintf(cmd1, "DEFSEG %s", name); + for (i=0; isegcnt; i++) { + sprintf(cmd1+strlen(cmd1), " %X-%X %s", + seg->range[i].start >> PAGE_SHIFT, + seg->range[i].end >> PAGE_SHIFT, + segtype_string[seg->range[i].start & 0xff]); + } + sprintf(cmd2, "SAVESEG %s", name); + cpcmd(cmd1, NULL, 0); + cpcmd(cmd2, NULL, 0); + spin_unlock(&dcss_lock); } EXPORT_SYMBOL(segment_load); EXPORT_SYMBOL(segment_unload); -EXPORT_SYMBOL(segment_replace); +EXPORT_SYMBOL(segment_save); +EXPORT_SYMBOL(segment_type); +EXPORT_SYMBOL(segment_modify_shared);