Commit 40221738 authored by Martin Schwidefsky's avatar Martin Schwidefsky Committed by Linus Torvalds

[PATCH] s390: dcss segments cleanup

From: Carsten Otte <cotte@de.ibm.com>
From: Gerald Schaefer <geraldsc@de.ibm.com>

Cleanup segment load/unload infrastructure.
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent f3351c55
/* /*
* File...........: arch/s390/mm/dcss.c * File...........: arch/s390/mm/extmem.c
* Author(s)......: Steven Shultz <shultzss@us.ibm.com> * Author(s)......: Carsten Otte <cotte@de.ibm.com>
* Carsten Otte <cotte@de.ibm.com> * Rob M van der Heij <rvdheij@nl.ibm.com>
* Steven Shultz <shultzss@us.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com>
* thanks to Rob M van der Heij * (C) IBM Corporation 2002-2004
* - he wrote the diag64 function
* (C) IBM Corporation 2002
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -43,18 +42,38 @@ ...@@ -43,18 +42,38 @@
#define DCSS_SEGEXT 0x18 #define DCSS_SEGEXT 0x18
#define DCSS_QACTV 0x0c #define DCSS_QACTV 0x0c
struct qout64 {
int segstart;
int segend;
int segcnt;
int segrcnt;
char segout[8][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 dcss_segment {
struct list_head list; struct list_head list;
char dcss_name[8]; char dcss_name[8];
unsigned long start_addr; unsigned long start_addr;
unsigned long end; unsigned long end;
atomic_t ref_count; atomic_t ref_count;
int dcss_attr; int do_nonshared;
int shared_attr; int vm_segtype;
}; };
static spinlock_t dcss_lock = SPIN_LOCK_UNLOCKED; static spinlock_t dcss_lock = SPIN_LOCK_UNLOCKED;
static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list); static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list);
static char *segtype_string[7] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC" };
extern struct { extern struct {
unsigned long addr, size, type; unsigned long addr, size, type;
} memory_chunk[MEMORY_CHUNKS]; } memory_chunk[MEMORY_CHUNKS];
...@@ -63,20 +82,46 @@ extern struct { ...@@ -63,20 +82,46 @@ extern struct {
* Create the 8 bytes, ebcdic VM segment name from * Create the 8 bytes, ebcdic VM segment name from
* an ascii name. * 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; int i;
for (i = 0; i <= 8; i++) { for (i = 0; i < 8; i++) {
if (name[i] == '\0') if (name[i] == '\0')
break; break;
dcss_name[i] = toupper(name[i]); dcss_name[i] = toupper(name[i]);
}; };
for (; i <= 8; i++) for (; i < 8; i++)
dcss_name[i] = ' '; dcss_name[i] = ' ';
ASCEBC(dcss_name, 8); 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)
{
char dcss_name[9];
struct list_head *l;
struct dcss_segment *tmp, *retval = NULL;
BUG_ON (!spin_is_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. * Perform a function on a dcss segment.
*/ */
...@@ -84,337 +129,270 @@ static inline int ...@@ -84,337 +129,270 @@ static inline int
dcss_diag (__u8 func, void *parameter, dcss_diag (__u8 func, void *parameter,
unsigned long *ret1, unsigned long *ret2) unsigned long *ret1, unsigned long *ret2)
{ {
unsigned long rx, ry; unsigned long rx, ry;
int rc; int rc;
rx = (unsigned long) parameter; rx = (unsigned long) parameter;
ry = (unsigned long) func; ry = (unsigned long) func;
__asm__ __volatile__( __asm__ __volatile__(
#ifdef CONFIG_ARCH_S390X #ifdef CONFIG_ARCH_S390X
" sam31\n" // switch to 31 bit " sam31\n" // switch to 31 bit
" diag %0,%1,0x64\n" " diag %0,%1,0x64\n"
" sam64\n" // switch back to 64 bit " sam64\n" // switch back to 64 bit
#else #else
" diag %0,%1,0x64\n" " diag %0,%1,0x64\n"
#endif #endif
" ipm %2\n" " ipm %2\n"
" srl %2,28\n" " srl %2,28\n"
: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" ); : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" );
*ret1 = rx; *ret1 = rx;
*ret2 = ry; *ret2 = ry;
return rc; return rc;
} }
/* use to issue "extended" dcss query */
static inline int static inline int
dcss_diag_query(char *name, int *rwattr, int *shattr, unsigned long *segstart, unsigned long *segend) 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_info (struct dcss_segment *seg)
{ {
int i,j,rc; struct qin64 *qin = kmalloc (sizeof(struct qin64), GFP_DMA);
unsigned long rx, ry; struct qout64 *qout = kmalloc (sizeof(struct qout64), GFP_DMA);
typedef struct segentry { int diag_cc, rc;
char thisseg[8]; unsigned long dummy, vmrc;
} segentry;
if ((qin == NULL) || (qout == NULL)) {
struct qout64 { rc = -ENOMEM;
int segstart; goto out_free;
int segend; }
int segcnt;
int segrcnt; /* initialize diag input parameters */
segentry segout[6]; qin->qopcode = DCSS_QACTV;
}; qin->qoutptr = (unsigned long) qout;
qin->qoutlen = sizeof(struct qout64);
struct qin64 { memcpy (qin->qname, seg->dcss_name, 8);
char qopcode;
char rsrv1[3]; diag_cc = dcss_diag (DCSS_SEGEXT, qin, &dummy, &vmrc);
char qrcode;
char rsrv2[3]; if (diag_cc > 1) {
char qname[8]; rc = dcss_diag_translate_rc (vmrc);
unsigned int qoutptr; goto out_free;
short int qoutlen; }
};
if (qout->segcnt > 1) {
rc = -ENOTSUPP;
struct qin64 *qinarea; goto out_free;
struct qout64 *qoutarea; }
qinarea = (struct qin64*) get_zeroed_page (GFP_DMA); /* analyze diag output and update seg */
if (!qinarea) { seg->start_addr = qout->segstart;
rc =-ENOMEM; seg->end = qout->segend;
goto out;
} seg->vm_segtype = qout->segout[0][3];
qoutarea = (struct qout64*) get_zeroed_page (GFP_DMA);
if (!qoutarea) { rc = 0;
rc = -ENOMEM;
free_page ((unsigned long) qinarea); out_free:
goto out; if (qin) kfree(qin);
} if (qout) kfree(qout);
memset (qinarea,0,PAGE_SIZE); return rc;
memset (qoutarea,0,PAGE_SIZE); }
qinarea->qopcode = DCSS_QACTV; /* do a query for active /*
segments */ * check if the given segment collides with guest storage.
qinarea->qoutptr = (unsigned long) qoutarea; * returns 1 if this is the case, 0 if no collision was found
qinarea->qoutlen = sizeof(struct qout64); */
static int
/* Move segment name into double word aligned segment_overlaps_storage(struct dcss_segment *seg)
field and pad with blanks to 8 long. {
*/ int i;
for (i = j = 0 ; i < 8; i++) { for (i=0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) {
qinarea->qname[i] = (name[j] == '\0') ? ' ' : name[j++]; if (memory_chunk[i].type != 0)
} continue;
if ((memory_chunk[i].addr >> 20) > (seg->end >> 20))
/* name already in EBCDIC */ continue;
/* ASCEBC ((void *)&qinarea.qname, 8); */ if (((memory_chunk[i].addr + memory_chunk[i].size - 1) >> 20)
< (seg->start_addr >> 20))
/* set the assembler variables */ continue;
rx = (unsigned long) qinarea; return 1;
ry = DCSS_SEGEXT; /* this is extended function */ }
return 0;
/* issue diagnose x'64' */ }
__asm__ __volatile__(
#ifdef CONFIG_ARCH_S390X /*
" sam31\n" // switch to 31 bit * check if segment collides with other segments that are currently loaded
" diag %0,%1,0x64\n" * returns 1 if this is the case, 0 if no collision was found
" sam64\n" // switch back to 64 bit */
#else static int
" diag %0,%1,0x64\n" segment_overlaps_others (struct dcss_segment *seg)
#endif {
" ipm %2\n" struct list_head *l;
" srl %2,28\n" struct dcss_segment *tmp;
: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc" );
BUG_ON (!spin_is_locked(&dcss_lock));
/* parse the query output area */ list_for_each(l, &dcss_list) {
*segstart=qoutarea->segstart; tmp = list_entry(l, struct dcss_segment, list);
*segend=qoutarea->segend; if ((tmp->start_addr >> 20) > (seg->end >> 20))
continue;
if (rc > 1) if ((tmp->end >> 20) < (seg->start_addr >> 20))
{ continue;
*rwattr = 2; if (seg == tmp)
*shattr = 2; continue;
rc = 0; return 1;
goto free; }
} return 0;
}
if (qoutarea->segcnt > 6)
{ /*
*rwattr = 3; * get info about a segment
*shattr = 3; * possible return values:
rc = 0; * -ENOSYS : we are not running on VM
goto free; * -EIO : could not perform query diagnose
} * -ENOENT : no such segment
* -ENOTSUPP: multi-part segment cannot be used with linux
*rwattr = 1; * -ENOSPC : segment cannot be used (overlaps with storage)
*shattr = 1; * -ENOMEM : out of memory
* 0 .. 6 : type of segment as defined in include/asm-s390/extmem.h
for (i=0; i < qoutarea->segrcnt; i++) { */
if (qoutarea->segout[i].thisseg[3] == 2 || int
qoutarea->segout[i].thisseg[3] == 3 || segment_info (char* name)
qoutarea->segout[i].thisseg[3] == 6 ) {
*rwattr = 0; int rc;
if (qoutarea->segout[i].thisseg[3] == 1 || struct dcss_segment seg;
qoutarea->segout[i].thisseg[3] == 3 ||
qoutarea->segout[i].thisseg[3] == 5 ) if (!MACHINE_IS_VM)
*shattr = 0; return -ENOSYS;
} /* end of for statement */
rc = 0; dcss_mkname(name, seg.dcss_name);
free: rc = query_segment_info (&seg);
free_page ((unsigned long) qoutarea); if (rc < 0)
free_page ((unsigned long) qinarea); 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_info (seg);
if (rc < 0)
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: 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)
* -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, int
unsigned long *end) segment_load (char *name, int do_nonshared, unsigned long *addr,
unsigned long *end)
{ {
char dcss_name[8]; struct dcss_segment *seg;
struct list_head *l; int rc;
struct dcss_segment *seg, *tmp;
unsigned long dummy; if (!MACHINE_IS_VM)
unsigned long segstart, segend; return -ENOSYS;
int rc = 0,i;
int rwattr, shattr; spin_lock (&dcss_lock);
seg = segment_by_name (name);
if (!MACHINE_IS_VM) if (seg == NULL)
return -ENOSYS; rc = __segment_load (name, do_nonshared, addr, end);
dcss_mkname(name, dcss_name); else {
/* search for the dcss in list of currently loaded segments */ if (do_nonshared == seg->do_nonshared) {
spin_lock(&dcss_lock); atomic_inc(&seg->ref_count);
seg = NULL; *addr = seg->start_addr;
list_for_each(l, &dcss_list) { *end = seg->end;
tmp = list_entry(l, struct dcss_segment, list); rc = seg->vm_segtype;
if (memcmp(tmp->dcss_name, dcss_name, 8) == 0) { } else {
seg = tmp; *addr = *end = 0;
break; rc = -EPERM;
}
}
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) { spin_unlock (&dcss_lock);
tmp = list_entry(l, struct dcss_segment, list); return rc;
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;*/
}
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;
} }
/* /*
...@@ -422,84 +400,65 @@ int segment_load(char *name, int segtype, unsigned long *addr, ...@@ -422,84 +400,65 @@ int segment_load(char *name, int segtype, unsigned long *addr,
* it from the address space if nobody is using it * it from the address space if nobody is using it
* any longer. * any longer.
*/ */
void segment_unload(char *name) void
segment_unload(char *name)
{ {
char dcss_name[8]; unsigned long dummy;
unsigned long dummy; struct dcss_segment *seg;
struct list_head *l,*l_tmp;
struct dcss_segment *seg; if (!MACHINE_IS_VM)
return;
if (!MACHINE_IS_VM)
return; spin_lock(&dcss_lock);
dcss_mkname(name, dcss_name); seg = segment_by_name (name);
spin_lock(&dcss_lock); if (seg == NULL) {
list_for_each_safe(l, l_tmp, &dcss_list) { PRINT_ERR ("could not find segment %s in segment_unload, "
seg = list_entry(l, struct dcss_segment, list); "please report to linux390@de.ibm.com\n",name);
if (memcmp(seg->dcss_name, dcss_name, 8) == 0) { goto out_unlock;
if (atomic_dec_return(&seg->ref_count) == 0) { }
/* Last user of the segment is if (atomic_dec_return(&seg->ref_count) == 0) {
gone. */ list_del(&seg->list);
list_del(&seg->list); dcss_diag(DCSS_PURGESEG, seg->dcss_name,
dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
&dummy, &dummy); kfree(seg);
kfree(seg); }
} out_unlock:
break; spin_unlock(&dcss_lock);
}
}
spin_unlock(&dcss_lock);
} }
/* /*
* Replace an existing DCSS segment, so that machines * save segment content permanently
* that load it anew will see the new version.
*/ */
void segment_replace(char *name) void segment_save(char *name)
{ {
char dcss_name[8]; struct dcss_segment *seg;
struct list_head *l; int startpfn = 0;
struct dcss_segment *seg; int endpfn = 0;
int mybeg = 0; char cmd1[80];
int myend = 0; char cmd2[80];
char mybuff1[80];
char mybuff2[80]; if (!MACHINE_IS_VM)
return;
if (!MACHINE_IS_VM)
return; spin_lock(&dcss_lock);
dcss_mkname(name, dcss_name); seg = segment_by_name (name);
memset (mybuff1, 0, sizeof(mybuff1)); if (seg == NULL) {
memset (mybuff2, 0, sizeof(mybuff2)); PRINT_ERR ("could not find segment %s in segment_save, please report to linux390@de.ibm.com\n",name);
return;
spin_lock(&dcss_lock); }
list_for_each(l, &dcss_list) {
seg = list_entry(l, struct dcss_segment, list); startpfn = seg->start_addr >> 12;
if (memcmp(seg->dcss_name, dcss_name, 8) == 0) { endpfn = (seg->end) >> 12;
mybeg = seg->start_addr >> 12; sprintf(cmd1, "DEFSEG %s %X-%X %s", name, startpfn, endpfn,
myend = (seg->end) >> 12; segtype_string[seg->vm_segtype]);
if (seg->shared_attr == SEGMENT_EXCLUSIVE_RW) sprintf(cmd2, "SAVESEG %s", name);
sprintf(mybuff1, "DEFSEG %s %X-%X EW", cpcmd(cmd1, NULL, 80);
name, mybeg, myend); cpcmd(cmd2, NULL, 80);
if (seg->shared_attr == SEGMENT_EXCLUSIVE_RO) spin_unlock(&dcss_lock);
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);
} }
EXPORT_SYMBOL(segment_load); EXPORT_SYMBOL(segment_load);
EXPORT_SYMBOL(segment_unload); EXPORT_SYMBOL(segment_unload);
EXPORT_SYMBOL(segment_replace); EXPORT_SYMBOL(segment_save);
EXPORT_SYMBOL(segment_info);
...@@ -139,6 +139,53 @@ dcssblk_get_device_by_name(char *name) ...@@ -139,6 +139,53 @@ dcssblk_get_device_by_name(char *name)
return NULL; return NULL;
} }
/*
* print appropriate error message for segment_load()/segment_info()
* return code
*/
static void
dcssblk_segment_warn(int rc, char* seg_name)
{
switch (rc) {
case -ENOENT:
PRINT_WARN("cannot load/query segment %s, does not exist\n",
seg_name);
break;
case -ENOSYS:
PRINT_WARN("cannot load/query segment %s, not running on VM\n",
seg_name);
break;
case -EIO:
PRINT_WARN("cannot load/query segment %s, hardware error\n",
seg_name);
break;
case -ENOTSUPP:
PRINT_WARN("cannot load/query segment %s, is a multi-part "
"segment\n", seg_name);
break;
case -ENOSPC:
PRINT_WARN("cannot load/query segment %s, overlaps with "
"storage\n", seg_name);
break;
case -EBUSY:
PRINT_WARN("cannot load/query segment %s, overlaps with "
"already loaded dcss\n", seg_name);
break;
case -EPERM:
PRINT_WARN("cannot load/query segment %s, already loaded in "
"incompatible mode\n", seg_name);
break;
case -ENOMEM:
PRINT_WARN("cannot load/query segment %s, out of memory\n",
seg_name);
break;
default:
PRINT_WARN("cannot load/query segment %s, return value %i\n",
seg_name, rc);
break;
}
}
/* /*
* device attribute for switching shared/nonshared (exclusive) * device attribute for switching shared/nonshared (exclusive)
* operation (show + store) * operation (show + store)
...@@ -185,61 +232,38 @@ dcssblk_shared_store(struct device *dev, const char *inbuf, size_t count) ...@@ -185,61 +232,38 @@ dcssblk_shared_store(struct device *dev, const char *inbuf, size_t count)
if (inbuf[0] == '1') { if (inbuf[0] == '1') {
// reload segment in shared mode // reload segment in shared mode
segment_unload(dev_info->segment_name); segment_unload(dev_info->segment_name);
rc = segment_load(dev_info->segment_name, SEGMENT_SHARED_RO, rc = segment_load(dev_info->segment_name, SEGMENT_SHARED,
&dev_info->start, &dev_info->end); &dev_info->start, &dev_info->end);
if (rc < 0) { if (rc < 0) {
PRINT_ERR("Segment %s not reloaded, rc=%d\n", dcssblk_segment_warn(rc, dev_info->segment_name);
dev_info->segment_name, rc);
goto removeseg; goto removeseg;
} }
dev_info->is_shared = 1; dev_info->is_shared = 1;
PRINT_INFO("Segment %s reloaded, shared mode.\n", if (rc == SEG_TYPE_SR || rc == SEG_TYPE_ER || rc == SEG_TYPE_SC)
dev_info->segment_name); set_disk_ro(dev_info->gd, 1);
} else if (inbuf[0] == '0') { } else if (inbuf[0] == '0') {
// reload segment in exclusive mode // reload segment in exclusive mode
if (dev_info->segment_type == SEG_TYPE_SC) {
PRINT_ERR("Segment type SC (%s) cannot be loaded in "
"non-shared mode\n", dev_info->segment_name);
up_write(&dcssblk_devices_sem);
return -EINVAL;
}
segment_unload(dev_info->segment_name); segment_unload(dev_info->segment_name);
rc = segment_load(dev_info->segment_name, SEGMENT_EXCLUSIVE_RW, rc = segment_load(dev_info->segment_name, SEGMENT_EXCLUSIVE,
&dev_info->start, &dev_info->end); &dev_info->start, &dev_info->end);
if (rc < 0) { if (rc < 0) {
PRINT_ERR("Segment %s not reloaded, rc=%d\n", dcssblk_segment_warn(rc, dev_info->segment_name);
dev_info->segment_name, rc);
goto removeseg; goto removeseg;
} }
dev_info->is_shared = 0; dev_info->is_shared = 0;
PRINT_INFO("Segment %s reloaded, exclusive (read-write) mode.\n", set_disk_ro(dev_info->gd, 0);
dev_info->segment_name);
} else { } else {
up_write(&dcssblk_devices_sem); up_write(&dcssblk_devices_sem);
PRINT_WARN("Invalid value, must be 0 or 1\n"); PRINT_WARN("Invalid value, must be 0 or 1\n");
return -EINVAL; return -EINVAL;
} }
dev_info->segment_type = rc;
rc = count; rc = count;
switch (dev_info->segment_type) {
case SEGMENT_SHARED_RO:
case SEGMENT_EXCLUSIVE_RO:
set_disk_ro(dev_info->gd, 1);
break;
case SEGMENT_SHARED_RW:
case SEGMENT_EXCLUSIVE_RW:
set_disk_ro(dev_info->gd, 0);
break;
}
if ((inbuf[0] == '1') &&
((dev_info->segment_type == SEGMENT_EXCLUSIVE_RO) ||
(dev_info->segment_type == SEGMENT_EXCLUSIVE_RW))) {
PRINT_WARN("Could not get shared copy of segment %s\n",
dev_info->segment_name);
rc = -EPERM;
}
if ((inbuf[0] == '0') &&
((dev_info->segment_type == SEGMENT_SHARED_RO) ||
(dev_info->segment_type == SEGMENT_SHARED_RW))) {
PRINT_WARN("Could not get exclusive copy of segment %s\n",
dev_info->segment_name);
rc = -EPERM;
}
up_write(&dcssblk_devices_sem); up_write(&dcssblk_devices_sem);
goto out; goto out;
...@@ -292,7 +316,7 @@ dcssblk_save_store(struct device *dev, const char *inbuf, size_t count) ...@@ -292,7 +316,7 @@ dcssblk_save_store(struct device *dev, const char *inbuf, size_t count)
// device is idle => we save immediately // device is idle => we save immediately
PRINT_INFO("Saving segment %s\n", PRINT_INFO("Saving segment %s\n",
dev_info->segment_name); dev_info->segment_name);
segment_replace(dev_info->segment_name); segment_save(dev_info->segment_name);
} else { } else {
// device is busy => we save it when it becomes // device is busy => we save it when it becomes
// idle in dcssblk_release // idle in dcssblk_release
...@@ -390,18 +414,17 @@ dcssblk_add_store(struct device *dev, const char *buf, size_t count) ...@@ -390,18 +414,17 @@ dcssblk_add_store(struct device *dev, const char *buf, size_t count)
/* /*
* load the segment * load the segment
*/ */
rc = segment_load(local_buf, SEGMENT_SHARED_RO, rc = segment_load(local_buf, SEGMENT_SHARED,
&dev_info->start, &dev_info->end); &dev_info->start, &dev_info->end);
if (rc < 0) { if (rc < 0) {
PRINT_ERR("Segment %s not loaded, rc=%d\n", local_buf, rc); dcssblk_segment_warn(rc, dev_info->segment_name);
goto dealloc_gendisk; goto dealloc_gendisk;
} }
seg_byte_size = (dev_info->end - dev_info->start + 1); seg_byte_size = (dev_info->end - dev_info->start + 1);
set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors
PRINT_INFO("Loaded segment %s from %p to %p, size = %lu Byte, " PRINT_INFO("Loaded segment %s, size = %lu Byte, "
"capacity = %lu sectors (512 Byte)\n", local_buf, "capacity = %lu (512 Byte) sectors\n", local_buf,
(void *) dev_info->start, (void *) dev_info->end, seg_byte_size, seg_byte_size >> 9);
seg_byte_size, seg_byte_size >> 9);
dev_info->segment_type = rc; dev_info->segment_type = rc;
dev_info->save_pending = 0; dev_info->save_pending = 0;
...@@ -451,12 +474,12 @@ dcssblk_add_store(struct device *dev, const char *buf, size_t count) ...@@ -451,12 +474,12 @@ dcssblk_add_store(struct device *dev, const char *buf, size_t count)
blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096); blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096);
switch (dev_info->segment_type) { switch (dev_info->segment_type) {
case SEGMENT_SHARED_RO: case SEG_TYPE_SR:
case SEGMENT_EXCLUSIVE_RO: case SEG_TYPE_ER:
case SEG_TYPE_SC:
set_disk_ro(dev_info->gd,1); set_disk_ro(dev_info->gd,1);
break; break;
case SEGMENT_SHARED_RW: default:
case SEGMENT_EXCLUSIVE_RW:
set_disk_ro(dev_info->gd,0); set_disk_ro(dev_info->gd,0);
break; break;
} }
...@@ -589,7 +612,7 @@ dcssblk_release(struct inode *inode, struct file *filp) ...@@ -589,7 +612,7 @@ dcssblk_release(struct inode *inode, struct file *filp)
&& (dev_info->save_pending)) { && (dev_info->save_pending)) {
PRINT_INFO("Segment %s became idle and is being saved now\n", PRINT_INFO("Segment %s became idle and is being saved now\n",
dev_info->segment_name); dev_info->segment_name);
segment_replace(dev_info->segment_name); segment_save(dev_info->segment_name);
dev_info->save_pending = 0; dev_info->save_pending = 0;
} }
up_write(&dcssblk_devices_sem); up_write(&dcssblk_devices_sem);
......
...@@ -115,6 +115,53 @@ dcss_mkname(char *ascii_name, char *ebcdic_name) ...@@ -115,6 +115,53 @@ dcss_mkname(char *ascii_name, char *ebcdic_name)
ASCEBC(ebcdic_name, 8); ASCEBC(ebcdic_name, 8);
} }
/*
* print appropriate error message for segment_load()/segment_info()
* return code
*/
static void
mon_segment_warn(int rc, char* seg_name)
{
switch (rc) {
case -ENOENT:
P_WARNING("cannot load/query segment %s, does not exist\n",
seg_name);
break;
case -ENOSYS:
P_WARNING("cannot load/query segment %s, not running on VM\n",
seg_name);
break;
case -EIO:
P_WARNING("cannot load/query segment %s, hardware error\n",
seg_name);
break;
case -ENOTSUPP:
P_WARNING("cannot load/query segment %s, is a multi-part "
"segment\n", seg_name);
break;
case -ENOSPC:
P_WARNING("cannot load/query segment %s, overlaps with "
"storage\n", seg_name);
break;
case -EBUSY:
P_WARNING("cannot load/query segment %s, overlaps with "
"already loaded dcss\n", seg_name);
break;
case -EPERM:
P_WARNING("cannot load/query segment %s, already loaded in "
"incompatible mode\n", seg_name);
break;
case -ENOMEM:
P_WARNING("cannot load/query segment %s, out of memory\n",
seg_name);
break;
default:
P_WARNING("cannot load/query segment %s, return value %i\n",
seg_name, rc);
break;
}
}
static inline unsigned long static inline unsigned long
mon_mca_start(struct mon_msg *monmsg) mon_mca_start(struct mon_msg *monmsg)
{ {
...@@ -534,10 +581,21 @@ mon_init(void) ...@@ -534,10 +581,21 @@ mon_init(void)
return -ENODEV; return -ENODEV;
} }
rc = segment_load(mon_dcss_name, SEGMENT_SHARED_RO, rc = segment_info(mon_dcss_name);
if (rc < 0) {
mon_segment_warn(rc, mon_dcss_name);
return rc;
}
if (rc != SEG_TYPE_SC) {
P_ERROR("segment %s has unsupported type, should be SC\n",
mon_dcss_name);
return -EINVAL;
}
rc = segment_load(mon_dcss_name, SEGMENT_SHARED,
&mon_dcss_start, &mon_dcss_end); &mon_dcss_start, &mon_dcss_end);
if (rc < 0) { if (rc < 0) {
P_ERROR("Segment %s not loaded, rc = %d\n", mon_dcss_name, rc); mon_segment_warn(rc, mon_dcss_name);
return -EINVAL; return -EINVAL;
} }
dcss_mkname(mon_dcss_name, &user_data_connect[8]); dcss_mkname(mon_dcss_name, &user_data_connect[8]);
......
...@@ -8,12 +8,23 @@ ...@@ -8,12 +8,23 @@
#ifndef _ASM_S390X_DCSS_H #ifndef _ASM_S390X_DCSS_H
#define _ASM_S390X_DCSS_H #define _ASM_S390X_DCSS_H
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#define SEGMENT_SHARED_RW 0
#define SEGMENT_SHARED_RO 1 /* possible values for segment type as returned by segment_info */
#define SEGMENT_EXCLUSIVE_RW 2 #define SEG_TYPE_SW 0
#define SEGMENT_EXCLUSIVE_RO 3 #define SEG_TYPE_EW 1
#define SEG_TYPE_SR 2
#define SEG_TYPE_ER 3
#define SEG_TYPE_SN 4
#define SEG_TYPE_EN 5
#define SEG_TYPE_SC 6
#define SEGMENT_SHARED 0
#define SEGMENT_EXCLUSIVE 1
extern int segment_load (char *name,int segtype,unsigned long *addr,unsigned long *length); extern int segment_load (char *name,int segtype,unsigned long *addr,unsigned long *length);
extern void segment_unload(char *name); extern void segment_unload(char *name);
extern void segment_replace(char *name); extern void segment_save(char *name);
extern int segment_info (char* name);
#endif #endif
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment