[S390] dcssblk: add >2G DCSSs support and stacked contiguous DCSSs support.
The DCSS block device driver is modified to add >2G DCSSs support and allow a DCSS block device to map to a set of contiguous DCSSs. The extmem code is also modified to use new Diagnose x'64' subcodes for >2G DCSSs. Signed-off-by: Hongjie Yang <hongjie@us.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Родитель
753c4dd6a2
Коммит
b2300b9efe
|
@ -43,21 +43,41 @@
|
||||||
#define DCSS_FINDSEG 0x0c
|
#define DCSS_FINDSEG 0x0c
|
||||||
#define DCSS_LOADNOLY 0x10
|
#define DCSS_LOADNOLY 0x10
|
||||||
#define DCSS_SEGEXT 0x18
|
#define DCSS_SEGEXT 0x18
|
||||||
|
#define DCSS_LOADSHRX 0x20
|
||||||
|
#define DCSS_LOADNSRX 0x24
|
||||||
|
#define DCSS_FINDSEGX 0x2c
|
||||||
|
#define DCSS_SEGEXTX 0x38
|
||||||
#define DCSS_FINDSEGA 0x0c
|
#define DCSS_FINDSEGA 0x0c
|
||||||
|
|
||||||
struct qrange {
|
struct qrange {
|
||||||
unsigned int start; // 3byte start address, 1 byte type
|
unsigned long start; /* last byte type */
|
||||||
unsigned int end; // 3byte end address, 1 byte reserved
|
unsigned long end; /* last byte reserved */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct qout64 {
|
struct qout64 {
|
||||||
int segstart;
|
unsigned long segstart;
|
||||||
int segend;
|
unsigned long segend;
|
||||||
int segcnt;
|
int segcnt;
|
||||||
int segrcnt;
|
int segrcnt;
|
||||||
struct qrange range[6];
|
struct qrange range[6];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
struct qrange_old {
|
||||||
|
unsigned int start; /* last byte type */
|
||||||
|
unsigned int end; /* last byte reserved */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* output area format for the Diag x'64' old subcode x'18' */
|
||||||
|
struct qout64_old {
|
||||||
|
int segstart;
|
||||||
|
int segend;
|
||||||
|
int segcnt;
|
||||||
|
int segrcnt;
|
||||||
|
struct qrange_old range[6];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
struct qin64 {
|
struct qin64 {
|
||||||
char qopcode;
|
char qopcode;
|
||||||
char rsrv1[3];
|
char rsrv1[3];
|
||||||
|
@ -86,6 +106,55 @@ static DEFINE_MUTEX(dcss_lock);
|
||||||
static LIST_HEAD(dcss_list);
|
static LIST_HEAD(dcss_list);
|
||||||
static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
|
static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
|
||||||
"EW/EN-MIXED" };
|
"EW/EN-MIXED" };
|
||||||
|
static int loadshr_scode, loadnsr_scode, findseg_scode;
|
||||||
|
static int segext_scode, purgeseg_scode;
|
||||||
|
static int scode_set;
|
||||||
|
|
||||||
|
/* set correct Diag x'64' subcodes. */
|
||||||
|
static int
|
||||||
|
dcss_set_subcodes(void)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
char *name = kmalloc(8 * sizeof(char), GFP_DMA);
|
||||||
|
unsigned long rx, ry;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (name == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rx = (unsigned long) name;
|
||||||
|
ry = DCSS_FINDSEGX;
|
||||||
|
|
||||||
|
strcpy(name, "dummy");
|
||||||
|
asm volatile(
|
||||||
|
" diag %0,%1,0x64\n"
|
||||||
|
"0: ipm %2\n"
|
||||||
|
" srl %2,28\n"
|
||||||
|
" j 2f\n"
|
||||||
|
"1: la %2,3\n"
|
||||||
|
"2:\n"
|
||||||
|
EX_TABLE(0b, 1b)
|
||||||
|
: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
|
||||||
|
|
||||||
|
kfree(name);
|
||||||
|
/* Diag x'64' new subcodes are supported, set to new subcodes */
|
||||||
|
if (rc != 3) {
|
||||||
|
loadshr_scode = DCSS_LOADSHRX;
|
||||||
|
loadnsr_scode = DCSS_LOADNSRX;
|
||||||
|
purgeseg_scode = DCSS_PURGESEG;
|
||||||
|
findseg_scode = DCSS_FINDSEGX;
|
||||||
|
segext_scode = DCSS_SEGEXTX;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* Diag x'64' new subcodes are not supported, set to old subcodes */
|
||||||
|
loadshr_scode = DCSS_LOADNOLY;
|
||||||
|
loadnsr_scode = DCSS_LOADNSR;
|
||||||
|
purgeseg_scode = DCSS_PURGESEG;
|
||||||
|
findseg_scode = DCSS_FINDSEG;
|
||||||
|
segext_scode = DCSS_SEGEXT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the 8 bytes, ebcdic VM segment name from
|
* Create the 8 bytes, ebcdic VM segment name from
|
||||||
|
@ -135,25 +204,45 @@ segment_by_name (char *name)
|
||||||
* Perform a function on a dcss segment.
|
* Perform a function on a dcss segment.
|
||||||
*/
|
*/
|
||||||
static inline int
|
static inline int
|
||||||
dcss_diag (__u8 func, void *parameter,
|
dcss_diag(int *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;
|
||||||
|
|
||||||
|
if (scode_set == 0) {
|
||||||
|
rc = dcss_set_subcodes();
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
scode_set = 1;
|
||||||
|
}
|
||||||
rx = (unsigned long) parameter;
|
rx = (unsigned long) parameter;
|
||||||
ry = (unsigned long) func;
|
ry = (unsigned long) *func;
|
||||||
asm volatile(
|
|
||||||
#ifdef CONFIG_64BIT
|
#ifdef CONFIG_64BIT
|
||||||
" sam31\n"
|
/* 64-bit Diag x'64' new subcode, keep in 64-bit addressing mode */
|
||||||
" diag %0,%1,0x64\n"
|
if (*func > DCSS_SEGEXT)
|
||||||
" sam64\n"
|
asm volatile(
|
||||||
|
" diag %0,%1,0x64\n"
|
||||||
|
" ipm %2\n"
|
||||||
|
" srl %2,28\n"
|
||||||
|
: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
|
||||||
|
/* 31-bit Diag x'64' old subcode, switch to 31-bit addressing mode */
|
||||||
|
else
|
||||||
|
asm volatile(
|
||||||
|
" sam31\n"
|
||||||
|
" diag %0,%1,0x64\n"
|
||||||
|
" sam64\n"
|
||||||
|
" ipm %2\n"
|
||||||
|
" srl %2,28\n"
|
||||||
|
: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
|
||||||
#else
|
#else
|
||||||
|
asm volatile(
|
||||||
" diag %0,%1,0x64\n"
|
" diag %0,%1,0x64\n"
|
||||||
#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");
|
||||||
|
#endif
|
||||||
*ret1 = rx;
|
*ret1 = rx;
|
||||||
*ret2 = ry;
|
*ret2 = ry;
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -190,14 +279,45 @@ query_segment_type (struct dcss_segment *seg)
|
||||||
qin->qoutlen = sizeof(struct qout64);
|
qin->qoutlen = sizeof(struct qout64);
|
||||||
memcpy (qin->qname, seg->dcss_name, 8);
|
memcpy (qin->qname, seg->dcss_name, 8);
|
||||||
|
|
||||||
diag_cc = dcss_diag (DCSS_SEGEXT, qin, &dummy, &vmrc);
|
diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc);
|
||||||
|
|
||||||
|
if (diag_cc < 0) {
|
||||||
|
rc = diag_cc;
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
if (diag_cc > 1) {
|
if (diag_cc > 1) {
|
||||||
PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc);
|
PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc);
|
||||||
rc = dcss_diag_translate_rc (vmrc);
|
rc = dcss_diag_translate_rc (vmrc);
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
/* Only old format of output area of Diagnose x'64' is supported,
|
||||||
|
copy data for the new format. */
|
||||||
|
if (segext_scode == DCSS_SEGEXT) {
|
||||||
|
struct qout64_old *qout_old;
|
||||||
|
qout_old = kzalloc(sizeof(struct qout64_old), GFP_DMA);
|
||||||
|
if (qout_old == NULL) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
memcpy(qout_old, qout, sizeof(struct qout64_old));
|
||||||
|
qout->segstart = (unsigned long) qout_old->segstart;
|
||||||
|
qout->segend = (unsigned long) qout_old->segend;
|
||||||
|
qout->segcnt = qout_old->segcnt;
|
||||||
|
qout->segrcnt = qout_old->segrcnt;
|
||||||
|
|
||||||
|
if (qout->segcnt > 6)
|
||||||
|
qout->segrcnt = 6;
|
||||||
|
for (i = 0; i < qout->segrcnt; i++) {
|
||||||
|
qout->range[i].start =
|
||||||
|
(unsigned long) qout_old->range[i].start;
|
||||||
|
qout->range[i].end =
|
||||||
|
(unsigned long) qout_old->range[i].end;
|
||||||
|
}
|
||||||
|
kfree(qout_old);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (qout->segcnt > 6) {
|
if (qout->segcnt > 6) {
|
||||||
rc = -ENOTSUPP;
|
rc = -ENOTSUPP;
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
@ -268,6 +388,30 @@ segment_type (char* name)
|
||||||
return seg.vm_segtype;
|
return seg.vm_segtype;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
BUG_ON(!mutex_is_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;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* real segment loading function, called from segment_load
|
* real segment loading function, called from segment_load
|
||||||
*/
|
*/
|
||||||
|
@ -276,7 +420,8 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
|
||||||
{
|
{
|
||||||
struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment),
|
struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment),
|
||||||
GFP_DMA);
|
GFP_DMA);
|
||||||
int dcss_command, rc, diag_cc;
|
int rc, diag_cc;
|
||||||
|
unsigned long start_addr, end_addr, dummy;
|
||||||
|
|
||||||
if (seg == NULL) {
|
if (seg == NULL) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
|
@ -287,6 +432,13 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
|
||||||
|
if (loadshr_scode == DCSS_LOADSHRX) {
|
||||||
|
if (segment_overlaps_others(seg)) {
|
||||||
|
rc = -EBUSY;
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
|
rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
|
||||||
|
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -316,20 +468,28 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_nonshared)
|
if (do_nonshared)
|
||||||
dcss_command = DCSS_LOADNSR;
|
diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
|
||||||
|
&start_addr, &end_addr);
|
||||||
else
|
else
|
||||||
dcss_command = DCSS_LOADNOLY;
|
diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
|
||||||
|
&start_addr, &end_addr);
|
||||||
diag_cc = dcss_diag(dcss_command, seg->dcss_name,
|
if (diag_cc < 0) {
|
||||||
&seg->start_addr, &seg->end);
|
dcss_diag(&purgeseg_scode, seg->dcss_name,
|
||||||
if (diag_cc > 1) {
|
&dummy, &dummy);
|
||||||
PRINT_WARN ("segment_load: could not load segment %s - "
|
rc = diag_cc;
|
||||||
"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_resource;
|
goto out_resource;
|
||||||
}
|
}
|
||||||
|
if (diag_cc > 1) {
|
||||||
|
PRINT_WARN ("segment_load: could not load segment %s - "
|
||||||
|
"diag returned error (%ld)\n",
|
||||||
|
name, end_addr);
|
||||||
|
rc = dcss_diag_translate_rc(end_addr);
|
||||||
|
dcss_diag(&purgeseg_scode, seg->dcss_name,
|
||||||
|
&dummy, &dummy);
|
||||||
|
goto out_resource;
|
||||||
|
}
|
||||||
|
seg->start_addr = start_addr;
|
||||||
|
seg->end = end_addr;
|
||||||
seg->do_nonshared = do_nonshared;
|
seg->do_nonshared = do_nonshared;
|
||||||
atomic_set(&seg->ref_count, 1);
|
atomic_set(&seg->ref_count, 1);
|
||||||
list_add(&seg->list, &dcss_list);
|
list_add(&seg->list, &dcss_list);
|
||||||
|
@ -423,8 +583,8 @@ int
|
||||||
segment_modify_shared (char *name, int do_nonshared)
|
segment_modify_shared (char *name, int do_nonshared)
|
||||||
{
|
{
|
||||||
struct dcss_segment *seg;
|
struct dcss_segment *seg;
|
||||||
unsigned long dummy;
|
unsigned long start_addr, end_addr, dummy;
|
||||||
int dcss_command, rc, diag_cc;
|
int rc, diag_cc;
|
||||||
|
|
||||||
mutex_lock(&dcss_lock);
|
mutex_lock(&dcss_lock);
|
||||||
seg = segment_by_name (name);
|
seg = segment_by_name (name);
|
||||||
|
@ -445,38 +605,51 @@ segment_modify_shared (char *name, int do_nonshared)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
release_resource(seg->res);
|
release_resource(seg->res);
|
||||||
if (do_nonshared) {
|
if (do_nonshared)
|
||||||
dcss_command = DCSS_LOADNSR;
|
|
||||||
seg->res->flags &= ~IORESOURCE_READONLY;
|
seg->res->flags &= ~IORESOURCE_READONLY;
|
||||||
} else {
|
else
|
||||||
dcss_command = DCSS_LOADNOLY;
|
|
||||||
if (seg->vm_segtype == SEG_TYPE_SR ||
|
if (seg->vm_segtype == SEG_TYPE_SR ||
|
||||||
seg->vm_segtype == SEG_TYPE_ER)
|
seg->vm_segtype == SEG_TYPE_ER)
|
||||||
seg->res->flags |= IORESOURCE_READONLY;
|
seg->res->flags |= IORESOURCE_READONLY;
|
||||||
}
|
|
||||||
if (request_resource(&iomem_resource, seg->res)) {
|
if (request_resource(&iomem_resource, seg->res)) {
|
||||||
PRINT_WARN("segment_modify_shared: could not reload segment %s"
|
PRINT_WARN("segment_modify_shared: could not reload segment %s"
|
||||||
" - overlapping resources\n", name);
|
" - overlapping resources\n", name);
|
||||||
rc = -EBUSY;
|
rc = -EBUSY;
|
||||||
kfree(seg->res);
|
kfree(seg->res);
|
||||||
goto out_del;
|
goto out_del_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
|
||||||
|
if (do_nonshared)
|
||||||
|
diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
|
||||||
|
&start_addr, &end_addr);
|
||||||
|
else
|
||||||
|
diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
|
||||||
|
&start_addr, &end_addr);
|
||||||
|
if (diag_cc < 0) {
|
||||||
|
rc = diag_cc;
|
||||||
|
goto out_del_res;
|
||||||
}
|
}
|
||||||
dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
|
|
||||||
diag_cc = dcss_diag(dcss_command, seg->dcss_name,
|
|
||||||
&seg->start_addr, &seg->end);
|
|
||||||
if (diag_cc > 1) {
|
if (diag_cc > 1) {
|
||||||
PRINT_WARN ("segment_modify_shared: could not reload segment %s"
|
PRINT_WARN ("segment_modify_shared: could not reload segment %s"
|
||||||
" - diag returned error (%ld)\n",name,seg->end);
|
" - diag returned error (%ld)\n",
|
||||||
rc = dcss_diag_translate_rc (seg->end);
|
name, end_addr);
|
||||||
goto out_del;
|
rc = dcss_diag_translate_rc(end_addr);
|
||||||
|
goto out_del_res;
|
||||||
}
|
}
|
||||||
|
seg->start_addr = start_addr;
|
||||||
|
seg->end = end_addr;
|
||||||
seg->do_nonshared = do_nonshared;
|
seg->do_nonshared = do_nonshared;
|
||||||
rc = 0;
|
rc = 0;
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
out_del:
|
out_del_res:
|
||||||
|
release_resource(seg->res);
|
||||||
|
kfree(seg->res);
|
||||||
|
out_del_mem:
|
||||||
vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
|
vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
|
||||||
list_del(&seg->list);
|
list_del(&seg->list);
|
||||||
dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
|
dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
|
||||||
kfree(seg);
|
kfree(seg);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
mutex_unlock(&dcss_lock);
|
mutex_unlock(&dcss_lock);
|
||||||
|
@ -510,7 +683,7 @@ segment_unload(char *name)
|
||||||
kfree(seg->res);
|
kfree(seg->res);
|
||||||
vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
|
vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
|
||||||
list_del(&seg->list);
|
list_del(&seg->list);
|
||||||
dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
|
dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
|
||||||
kfree(seg);
|
kfree(seg);
|
||||||
out_unlock:
|
out_unlock:
|
||||||
mutex_unlock(&dcss_lock);
|
mutex_unlock(&dcss_lock);
|
||||||
|
@ -545,7 +718,7 @@ segment_save(char *name)
|
||||||
endpfn = (seg->end) >> PAGE_SHIFT;
|
endpfn = (seg->end) >> PAGE_SHIFT;
|
||||||
sprintf(cmd1, "DEFSEG %s", name);
|
sprintf(cmd1, "DEFSEG %s", name);
|
||||||
for (i=0; i<seg->segcnt; i++) {
|
for (i=0; i<seg->segcnt; i++) {
|
||||||
sprintf(cmd1+strlen(cmd1), " %X-%X %s",
|
sprintf(cmd1+strlen(cmd1), " %lX-%lX %s",
|
||||||
seg->range[i].start >> PAGE_SHIFT,
|
seg->range[i].start >> PAGE_SHIFT,
|
||||||
seg->range[i].end >> PAGE_SHIFT,
|
seg->range[i].end >> PAGE_SHIFT,
|
||||||
segtype_string[seg->range[i].start & 0xff]);
|
segtype_string[seg->range[i].start & 0xff]);
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
#define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x)
|
#define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x)
|
||||||
#define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x)
|
#define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x)
|
||||||
|
|
||||||
|
|
||||||
static int dcssblk_open(struct inode *inode, struct file *filp);
|
static int dcssblk_open(struct inode *inode, struct file *filp);
|
||||||
static int dcssblk_release(struct inode *inode, struct file *filp);
|
static int dcssblk_release(struct inode *inode, struct file *filp);
|
||||||
static int dcssblk_make_request(struct request_queue *q, struct bio *bio);
|
static int dcssblk_make_request(struct request_queue *q, struct bio *bio);
|
||||||
|
@ -48,26 +47,6 @@ static struct block_device_operations dcssblk_devops = {
|
||||||
.direct_access = dcssblk_direct_access,
|
.direct_access = dcssblk_direct_access,
|
||||||
};
|
};
|
||||||
|
|
||||||
static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf,
|
|
||||||
size_t count);
|
|
||||||
static ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf,
|
|
||||||
size_t count);
|
|
||||||
static ssize_t dcssblk_save_store(struct device * dev, struct device_attribute *attr, const char * buf,
|
|
||||||
size_t count);
|
|
||||||
static ssize_t dcssblk_save_show(struct device *dev, struct device_attribute *attr, char *buf);
|
|
||||||
static ssize_t dcssblk_shared_store(struct device * dev, struct device_attribute *attr, const char * buf,
|
|
||||||
size_t count);
|
|
||||||
static ssize_t dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf);
|
|
||||||
|
|
||||||
static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store);
|
|
||||||
static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store);
|
|
||||||
static DEVICE_ATTR(save, S_IWUSR | S_IRUGO, dcssblk_save_show,
|
|
||||||
dcssblk_save_store);
|
|
||||||
static DEVICE_ATTR(shared, S_IWUSR | S_IRUGO, dcssblk_shared_show,
|
|
||||||
dcssblk_shared_store);
|
|
||||||
|
|
||||||
static struct device *dcssblk_root_dev;
|
|
||||||
|
|
||||||
struct dcssblk_dev_info {
|
struct dcssblk_dev_info {
|
||||||
struct list_head lh;
|
struct list_head lh;
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
@ -80,8 +59,42 @@ struct dcssblk_dev_info {
|
||||||
unsigned char save_pending;
|
unsigned char save_pending;
|
||||||
unsigned char is_shared;
|
unsigned char is_shared;
|
||||||
struct request_queue *dcssblk_queue;
|
struct request_queue *dcssblk_queue;
|
||||||
|
int num_of_segments;
|
||||||
|
struct list_head seg_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct segment_info {
|
||||||
|
struct list_head lh;
|
||||||
|
char segment_name[BUS_ID_SIZE];
|
||||||
|
unsigned long start;
|
||||||
|
unsigned long end;
|
||||||
|
int segment_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf,
|
||||||
|
size_t count);
|
||||||
|
static ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf,
|
||||||
|
size_t count);
|
||||||
|
static ssize_t dcssblk_save_store(struct device * dev, struct device_attribute *attr, const char * buf,
|
||||||
|
size_t count);
|
||||||
|
static ssize_t dcssblk_save_show(struct device *dev, struct device_attribute *attr, char *buf);
|
||||||
|
static ssize_t dcssblk_shared_store(struct device * dev, struct device_attribute *attr, const char * buf,
|
||||||
|
size_t count);
|
||||||
|
static ssize_t dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf);
|
||||||
|
static ssize_t dcssblk_seglist_show(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf);
|
||||||
|
|
||||||
|
static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store);
|
||||||
|
static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store);
|
||||||
|
static DEVICE_ATTR(save, S_IWUSR | S_IRUSR, dcssblk_save_show,
|
||||||
|
dcssblk_save_store);
|
||||||
|
static DEVICE_ATTR(shared, S_IWUSR | S_IRUSR, dcssblk_shared_show,
|
||||||
|
dcssblk_shared_store);
|
||||||
|
static DEVICE_ATTR(seglist, S_IRUSR, dcssblk_seglist_show, NULL);
|
||||||
|
|
||||||
|
static struct device *dcssblk_root_dev;
|
||||||
|
|
||||||
static LIST_HEAD(dcssblk_devices);
|
static LIST_HEAD(dcssblk_devices);
|
||||||
static struct rw_semaphore dcssblk_devices_sem;
|
static struct rw_semaphore dcssblk_devices_sem;
|
||||||
|
|
||||||
|
@ -91,8 +104,15 @@ static struct rw_semaphore dcssblk_devices_sem;
|
||||||
static void
|
static void
|
||||||
dcssblk_release_segment(struct device *dev)
|
dcssblk_release_segment(struct device *dev)
|
||||||
{
|
{
|
||||||
PRINT_DEBUG("segment release fn called for %s\n", dev_name(dev));
|
struct dcssblk_dev_info *dev_info;
|
||||||
kfree(container_of(dev, struct dcssblk_dev_info, dev));
|
struct segment_info *entry, *temp;
|
||||||
|
|
||||||
|
dev_info = container_of(dev, struct dcssblk_dev_info, dev);
|
||||||
|
list_for_each_entry_safe(entry, temp, &dev_info->seg_list, lh) {
|
||||||
|
list_del(&entry->lh);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
kfree(dev_info);
|
||||||
module_put(THIS_MODULE);
|
module_put(THIS_MODULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,6 +162,169 @@ dcssblk_get_device_by_name(char *name)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get the struct segment_info from seg_list
|
||||||
|
* for the given name.
|
||||||
|
* down_read(&dcssblk_devices_sem) must be held.
|
||||||
|
*/
|
||||||
|
static struct segment_info *
|
||||||
|
dcssblk_get_segment_by_name(char *name)
|
||||||
|
{
|
||||||
|
struct dcssblk_dev_info *dev_info;
|
||||||
|
struct segment_info *entry;
|
||||||
|
|
||||||
|
list_for_each_entry(dev_info, &dcssblk_devices, lh) {
|
||||||
|
list_for_each_entry(entry, &dev_info->seg_list, lh) {
|
||||||
|
if (!strcmp(name, entry->segment_name))
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get the highest address of the multi-segment block.
|
||||||
|
*/
|
||||||
|
static unsigned long
|
||||||
|
dcssblk_find_highest_addr(struct dcssblk_dev_info *dev_info)
|
||||||
|
{
|
||||||
|
unsigned long highest_addr;
|
||||||
|
struct segment_info *entry;
|
||||||
|
|
||||||
|
highest_addr = 0;
|
||||||
|
list_for_each_entry(entry, &dev_info->seg_list, lh) {
|
||||||
|
if (highest_addr < entry->end)
|
||||||
|
highest_addr = entry->end;
|
||||||
|
}
|
||||||
|
return highest_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get the lowest address of the multi-segment block.
|
||||||
|
*/
|
||||||
|
static unsigned long
|
||||||
|
dcssblk_find_lowest_addr(struct dcssblk_dev_info *dev_info)
|
||||||
|
{
|
||||||
|
int set_first;
|
||||||
|
unsigned long lowest_addr;
|
||||||
|
struct segment_info *entry;
|
||||||
|
|
||||||
|
set_first = 0;
|
||||||
|
lowest_addr = 0;
|
||||||
|
list_for_each_entry(entry, &dev_info->seg_list, lh) {
|
||||||
|
if (set_first == 0) {
|
||||||
|
lowest_addr = entry->start;
|
||||||
|
set_first = 1;
|
||||||
|
} else {
|
||||||
|
if (lowest_addr > entry->start)
|
||||||
|
lowest_addr = entry->start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lowest_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check continuity of segments.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
dcssblk_is_continuous(struct dcssblk_dev_info *dev_info)
|
||||||
|
{
|
||||||
|
int i, j, rc;
|
||||||
|
struct segment_info *sort_list, *entry, temp;
|
||||||
|
|
||||||
|
if (dev_info->num_of_segments <= 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
sort_list = kzalloc(
|
||||||
|
sizeof(struct segment_info) * dev_info->num_of_segments,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (sort_list == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
i = 0;
|
||||||
|
list_for_each_entry(entry, &dev_info->seg_list, lh) {
|
||||||
|
memcpy(&sort_list[i], entry, sizeof(struct segment_info));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sort segments */
|
||||||
|
for (i = 0; i < dev_info->num_of_segments; i++)
|
||||||
|
for (j = 0; j < dev_info->num_of_segments; j++)
|
||||||
|
if (sort_list[j].start > sort_list[i].start) {
|
||||||
|
memcpy(&temp, &sort_list[i],
|
||||||
|
sizeof(struct segment_info));
|
||||||
|
memcpy(&sort_list[i], &sort_list[j],
|
||||||
|
sizeof(struct segment_info));
|
||||||
|
memcpy(&sort_list[j], &temp,
|
||||||
|
sizeof(struct segment_info));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check continuity */
|
||||||
|
for (i = 0; i < dev_info->num_of_segments - 1; i++) {
|
||||||
|
if ((sort_list[i].end + 1) != sort_list[i+1].start) {
|
||||||
|
PRINT_ERR("Segment %s is not contiguous with "
|
||||||
|
"segment %s\n",
|
||||||
|
sort_list[i].segment_name,
|
||||||
|
sort_list[i+1].segment_name);
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
/* EN and EW are allowed in a block device */
|
||||||
|
if (sort_list[i].segment_type != sort_list[i+1].segment_type) {
|
||||||
|
if (!(sort_list[i].segment_type & SEGMENT_EXCLUSIVE) ||
|
||||||
|
(sort_list[i].segment_type == SEG_TYPE_ER) ||
|
||||||
|
!(sort_list[i+1].segment_type &
|
||||||
|
SEGMENT_EXCLUSIVE) ||
|
||||||
|
(sort_list[i+1].segment_type == SEG_TYPE_ER)) {
|
||||||
|
PRINT_ERR("Segment %s has different type from "
|
||||||
|
"segment %s\n",
|
||||||
|
sort_list[i].segment_name,
|
||||||
|
sort_list[i+1].segment_name);
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rc = 0;
|
||||||
|
out:
|
||||||
|
kfree(sort_list);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load a segment
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
dcssblk_load_segment(char *name, struct segment_info **seg_info)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* already loaded? */
|
||||||
|
down_read(&dcssblk_devices_sem);
|
||||||
|
*seg_info = dcssblk_get_segment_by_name(name);
|
||||||
|
up_read(&dcssblk_devices_sem);
|
||||||
|
if (*seg_info != NULL)
|
||||||
|
return -EEXIST;
|
||||||
|
|
||||||
|
/* get a struct segment_info */
|
||||||
|
*seg_info = kzalloc(sizeof(struct segment_info), GFP_KERNEL);
|
||||||
|
if (*seg_info == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
strcpy((*seg_info)->segment_name, name);
|
||||||
|
|
||||||
|
/* load the segment */
|
||||||
|
rc = segment_load(name, SEGMENT_SHARED,
|
||||||
|
&(*seg_info)->start, &(*seg_info)->end);
|
||||||
|
if (rc < 0) {
|
||||||
|
segment_warning(rc, (*seg_info)->segment_name);
|
||||||
|
kfree(*seg_info);
|
||||||
|
} else {
|
||||||
|
INIT_LIST_HEAD(&(*seg_info)->lh);
|
||||||
|
(*seg_info)->segment_type = rc;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static void dcssblk_unregister_callback(struct device *dev)
|
static void dcssblk_unregister_callback(struct device *dev)
|
||||||
{
|
{
|
||||||
device_unregister(dev);
|
device_unregister(dev);
|
||||||
|
@ -165,6 +348,7 @@ static ssize_t
|
||||||
dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
|
dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
|
||||||
{
|
{
|
||||||
struct dcssblk_dev_info *dev_info;
|
struct dcssblk_dev_info *dev_info;
|
||||||
|
struct segment_info *entry, *temp;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
|
if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
|
||||||
|
@ -172,46 +356,46 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch
|
||||||
down_write(&dcssblk_devices_sem);
|
down_write(&dcssblk_devices_sem);
|
||||||
dev_info = container_of(dev, struct dcssblk_dev_info, dev);
|
dev_info = container_of(dev, struct dcssblk_dev_info, dev);
|
||||||
if (atomic_read(&dev_info->use_count)) {
|
if (atomic_read(&dev_info->use_count)) {
|
||||||
PRINT_ERR("share: segment %s is busy!\n",
|
|
||||||
dev_info->segment_name);
|
|
||||||
rc = -EBUSY;
|
rc = -EBUSY;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (inbuf[0] == '1') {
|
if (inbuf[0] == '1') {
|
||||||
// reload segment in shared mode
|
/* reload segments in shared mode */
|
||||||
rc = segment_modify_shared(dev_info->segment_name,
|
list_for_each_entry(entry, &dev_info->seg_list, lh) {
|
||||||
SEGMENT_SHARED);
|
rc = segment_modify_shared(entry->segment_name,
|
||||||
if (rc < 0) {
|
SEGMENT_SHARED);
|
||||||
BUG_ON(rc == -EINVAL);
|
if (rc < 0) {
|
||||||
if (rc != -EAGAIN)
|
BUG_ON(rc == -EINVAL);
|
||||||
goto removeseg;
|
if (rc != -EAGAIN)
|
||||||
} else {
|
goto removeseg;
|
||||||
dev_info->is_shared = 1;
|
|
||||||
switch (dev_info->segment_type) {
|
|
||||||
case SEG_TYPE_SR:
|
|
||||||
case SEG_TYPE_ER:
|
|
||||||
case SEG_TYPE_SC:
|
|
||||||
set_disk_ro(dev_info->gd,1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dev_info->is_shared = 1;
|
||||||
|
switch (dev_info->segment_type) {
|
||||||
|
case SEG_TYPE_SR:
|
||||||
|
case SEG_TYPE_ER:
|
||||||
|
case SEG_TYPE_SC:
|
||||||
|
set_disk_ro(dev_info->gd, 1);
|
||||||
|
}
|
||||||
} else if (inbuf[0] == '0') {
|
} else if (inbuf[0] == '0') {
|
||||||
// reload segment in exclusive mode
|
/* reload segments in exclusive mode */
|
||||||
if (dev_info->segment_type == SEG_TYPE_SC) {
|
if (dev_info->segment_type == SEG_TYPE_SC) {
|
||||||
PRINT_ERR("Segment type SC (%s) cannot be loaded in "
|
PRINT_ERR("Segment type SC (%s) cannot be loaded in "
|
||||||
"non-shared mode\n", dev_info->segment_name);
|
"non-shared mode\n", dev_info->segment_name);
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
rc = segment_modify_shared(dev_info->segment_name,
|
list_for_each_entry(entry, &dev_info->seg_list, lh) {
|
||||||
SEGMENT_EXCLUSIVE);
|
rc = segment_modify_shared(entry->segment_name,
|
||||||
if (rc < 0) {
|
SEGMENT_EXCLUSIVE);
|
||||||
BUG_ON(rc == -EINVAL);
|
if (rc < 0) {
|
||||||
if (rc != -EAGAIN)
|
BUG_ON(rc == -EINVAL);
|
||||||
goto removeseg;
|
if (rc != -EAGAIN)
|
||||||
} else {
|
goto removeseg;
|
||||||
dev_info->is_shared = 0;
|
}
|
||||||
set_disk_ro(dev_info->gd, 0);
|
|
||||||
}
|
}
|
||||||
|
dev_info->is_shared = 0;
|
||||||
|
set_disk_ro(dev_info->gd, 0);
|
||||||
} else {
|
} else {
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -220,8 +404,14 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
removeseg:
|
removeseg:
|
||||||
PRINT_ERR("Could not reload segment %s, removing it now!\n",
|
PRINT_ERR("Could not reload segment(s) of the device %s, removing "
|
||||||
dev_info->segment_name);
|
"segment(s) now!\n",
|
||||||
|
dev_info->segment_name);
|
||||||
|
temp = entry;
|
||||||
|
list_for_each_entry(entry, &dev_info->seg_list, lh) {
|
||||||
|
if (entry != temp)
|
||||||
|
segment_unload(entry->segment_name);
|
||||||
|
}
|
||||||
list_del(&dev_info->lh);
|
list_del(&dev_info->lh);
|
||||||
|
|
||||||
del_gendisk(dev_info->gd);
|
del_gendisk(dev_info->gd);
|
||||||
|
@ -254,6 +444,7 @@ static ssize_t
|
||||||
dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
|
dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count)
|
||||||
{
|
{
|
||||||
struct dcssblk_dev_info *dev_info;
|
struct dcssblk_dev_info *dev_info;
|
||||||
|
struct segment_info *entry;
|
||||||
|
|
||||||
if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
|
if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -263,14 +454,16 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char
|
||||||
if (inbuf[0] == '1') {
|
if (inbuf[0] == '1') {
|
||||||
if (atomic_read(&dev_info->use_count) == 0) {
|
if (atomic_read(&dev_info->use_count) == 0) {
|
||||||
// device is idle => we save immediately
|
// device is idle => we save immediately
|
||||||
PRINT_INFO("Saving segment %s\n",
|
PRINT_INFO("Saving segment(s) of the device %s\n",
|
||||||
dev_info->segment_name);
|
dev_info->segment_name);
|
||||||
segment_save(dev_info->segment_name);
|
list_for_each_entry(entry, &dev_info->seg_list, lh) {
|
||||||
|
segment_save(entry->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
|
||||||
PRINT_INFO("Segment %s is currently busy, it will "
|
PRINT_INFO("Device %s is currently busy, segment(s) "
|
||||||
"be saved when it becomes idle...\n",
|
"will be saved when it becomes idle...\n",
|
||||||
dev_info->segment_name);
|
dev_info->segment_name);
|
||||||
dev_info->save_pending = 1;
|
dev_info->save_pending = 1;
|
||||||
}
|
}
|
||||||
|
@ -279,7 +472,8 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char
|
||||||
// device is busy & the user wants to undo his save
|
// device is busy & the user wants to undo his save
|
||||||
// request
|
// request
|
||||||
dev_info->save_pending = 0;
|
dev_info->save_pending = 0;
|
||||||
PRINT_INFO("Pending save for segment %s deactivated\n",
|
PRINT_INFO("Pending save for segment(s) of the device "
|
||||||
|
"%s deactivated\n",
|
||||||
dev_info->segment_name);
|
dev_info->segment_name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -290,67 +484,124 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* device attribute for showing all segments in a device
|
||||||
|
*/
|
||||||
|
static ssize_t
|
||||||
|
dcssblk_seglist_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
struct dcssblk_dev_info *dev_info;
|
||||||
|
struct segment_info *entry;
|
||||||
|
|
||||||
|
down_read(&dcssblk_devices_sem);
|
||||||
|
dev_info = container_of(dev, struct dcssblk_dev_info, dev);
|
||||||
|
i = 0;
|
||||||
|
buf[0] = '\0';
|
||||||
|
list_for_each_entry(entry, &dev_info->seg_list, lh) {
|
||||||
|
strcpy(&buf[i], entry->segment_name);
|
||||||
|
i += strlen(entry->segment_name);
|
||||||
|
buf[i] = '\n';
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
up_read(&dcssblk_devices_sem);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* device attribute for adding devices
|
* device attribute for adding devices
|
||||||
*/
|
*/
|
||||||
static ssize_t
|
static ssize_t
|
||||||
dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
int rc, i;
|
int rc, i, j, num_of_segments;
|
||||||
struct dcssblk_dev_info *dev_info;
|
struct dcssblk_dev_info *dev_info;
|
||||||
|
struct segment_info *seg_info, *temp;
|
||||||
char *local_buf;
|
char *local_buf;
|
||||||
unsigned long seg_byte_size;
|
unsigned long seg_byte_size;
|
||||||
|
|
||||||
dev_info = NULL;
|
dev_info = NULL;
|
||||||
|
seg_info = NULL;
|
||||||
if (dev != dcssblk_root_dev) {
|
if (dev != dcssblk_root_dev) {
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto out_nobuf;
|
goto out_nobuf;
|
||||||
}
|
}
|
||||||
|
if ((count < 1) || (buf[0] == '\0') || (buf[0] == '\n')) {
|
||||||
|
rc = -ENAMETOOLONG;
|
||||||
|
goto out_nobuf;
|
||||||
|
}
|
||||||
|
|
||||||
local_buf = kmalloc(count + 1, GFP_KERNEL);
|
local_buf = kmalloc(count + 1, GFP_KERNEL);
|
||||||
if (local_buf == NULL) {
|
if (local_buf == NULL) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto out_nobuf;
|
goto out_nobuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* parse input
|
* parse input
|
||||||
*/
|
*/
|
||||||
|
num_of_segments = 0;
|
||||||
for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) {
|
for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) {
|
||||||
local_buf[i] = toupper(buf[i]);
|
for (j = i; (buf[j] != ':') &&
|
||||||
}
|
(buf[j] != '\0') &&
|
||||||
local_buf[i] = '\0';
|
(buf[j] != '\n') &&
|
||||||
if ((i == 0) || (i > 8)) {
|
j < count; j++) {
|
||||||
rc = -ENAMETOOLONG;
|
local_buf[j-i] = toupper(buf[j]);
|
||||||
goto out;
|
}
|
||||||
}
|
local_buf[j-i] = '\0';
|
||||||
/*
|
if (((j - i) == 0) || ((j - i) > 8)) {
|
||||||
* already loaded?
|
rc = -ENAMETOOLONG;
|
||||||
*/
|
goto seg_list_del;
|
||||||
down_read(&dcssblk_devices_sem);
|
}
|
||||||
dev_info = dcssblk_get_device_by_name(local_buf);
|
|
||||||
up_read(&dcssblk_devices_sem);
|
rc = dcssblk_load_segment(local_buf, &seg_info);
|
||||||
if (dev_info != NULL) {
|
if (rc < 0)
|
||||||
PRINT_WARN("Segment %s already loaded!\n", local_buf);
|
goto seg_list_del;
|
||||||
rc = -EEXIST;
|
/*
|
||||||
goto out;
|
* get a struct dcssblk_dev_info
|
||||||
}
|
*/
|
||||||
/*
|
if (num_of_segments == 0) {
|
||||||
* get a struct dcssblk_dev_info
|
dev_info = kzalloc(sizeof(struct dcssblk_dev_info),
|
||||||
*/
|
GFP_KERNEL);
|
||||||
dev_info = kzalloc(sizeof(struct dcssblk_dev_info), GFP_KERNEL);
|
if (dev_info == NULL) {
|
||||||
if (dev_info == NULL) {
|
rc = -ENOMEM;
|
||||||
rc = -ENOMEM;
|
goto out;
|
||||||
goto out;
|
}
|
||||||
|
strcpy(dev_info->segment_name, local_buf);
|
||||||
|
dev_info->segment_type = seg_info->segment_type;
|
||||||
|
INIT_LIST_HEAD(&dev_info->seg_list);
|
||||||
|
}
|
||||||
|
list_add_tail(&seg_info->lh, &dev_info->seg_list);
|
||||||
|
num_of_segments++;
|
||||||
|
i = j;
|
||||||
|
|
||||||
|
if ((buf[j] == '\0') || (buf[j] == '\n'))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
strcpy(dev_info->segment_name, local_buf);
|
/* no trailing colon at the end of the input */
|
||||||
dev_set_name(&dev_info->dev, local_buf);
|
if ((i > 0) && (buf[i-1] == ':')) {
|
||||||
|
rc = -ENAMETOOLONG;
|
||||||
|
goto seg_list_del;
|
||||||
|
}
|
||||||
|
strlcpy(local_buf, buf, i + 1);
|
||||||
|
dev_info->num_of_segments = num_of_segments;
|
||||||
|
rc = dcssblk_is_continuous(dev_info);
|
||||||
|
if (rc < 0)
|
||||||
|
goto seg_list_del;
|
||||||
|
|
||||||
|
dev_info->start = dcssblk_find_lowest_addr(dev_info);
|
||||||
|
dev_info->end = dcssblk_find_highest_addr(dev_info);
|
||||||
|
|
||||||
|
dev_set_name(&dev_info->dev, dev_info->segment_name);
|
||||||
dev_info->dev.release = dcssblk_release_segment;
|
dev_info->dev.release = dcssblk_release_segment;
|
||||||
INIT_LIST_HEAD(&dev_info->lh);
|
INIT_LIST_HEAD(&dev_info->lh);
|
||||||
|
|
||||||
dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK);
|
dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK);
|
||||||
if (dev_info->gd == NULL) {
|
if (dev_info->gd == NULL) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto free_dev_info;
|
goto seg_list_del;
|
||||||
}
|
}
|
||||||
dev_info->gd->major = dcssblk_major;
|
dev_info->gd->major = dcssblk_major;
|
||||||
dev_info->gd->fops = &dcssblk_devops;
|
dev_info->gd->fops = &dcssblk_devops;
|
||||||
|
@ -360,65 +611,52 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
|
||||||
dev_info->gd->driverfs_dev = &dev_info->dev;
|
dev_info->gd->driverfs_dev = &dev_info->dev;
|
||||||
blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
|
blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
|
||||||
blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096);
|
blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096);
|
||||||
/*
|
|
||||||
* load the segment
|
|
||||||
*/
|
|
||||||
rc = segment_load(local_buf, SEGMENT_SHARED,
|
|
||||||
&dev_info->start, &dev_info->end);
|
|
||||||
if (rc < 0) {
|
|
||||||
segment_warning(rc, dev_info->segment_name);
|
|
||||||
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, size = %lu Byte, "
|
PRINT_INFO("Loaded segment(s) %s, size = %lu Byte, "
|
||||||
"capacity = %lu (512 Byte) sectors\n", local_buf,
|
"capacity = %lu (512 Byte) sectors\n", local_buf,
|
||||||
seg_byte_size, seg_byte_size >> 9);
|
seg_byte_size, seg_byte_size >> 9);
|
||||||
|
|
||||||
dev_info->segment_type = rc;
|
|
||||||
dev_info->save_pending = 0;
|
dev_info->save_pending = 0;
|
||||||
dev_info->is_shared = 1;
|
dev_info->is_shared = 1;
|
||||||
dev_info->dev.parent = dcssblk_root_dev;
|
dev_info->dev.parent = dcssblk_root_dev;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get minor, add to list
|
*get minor, add to list
|
||||||
*/
|
*/
|
||||||
down_write(&dcssblk_devices_sem);
|
down_write(&dcssblk_devices_sem);
|
||||||
if (dcssblk_get_device_by_name(local_buf)) {
|
if (dcssblk_get_segment_by_name(local_buf)) {
|
||||||
up_write(&dcssblk_devices_sem);
|
|
||||||
rc = -EEXIST;
|
rc = -EEXIST;
|
||||||
goto unload_seg;
|
goto release_gd;
|
||||||
}
|
}
|
||||||
rc = dcssblk_assign_free_minor(dev_info);
|
rc = dcssblk_assign_free_minor(dev_info);
|
||||||
if (rc) {
|
if (rc)
|
||||||
up_write(&dcssblk_devices_sem);
|
goto release_gd;
|
||||||
PRINT_ERR("No free minor number available! "
|
|
||||||
"Unloading segment...\n");
|
|
||||||
goto unload_seg;
|
|
||||||
}
|
|
||||||
sprintf(dev_info->gd->disk_name, "dcssblk%d",
|
sprintf(dev_info->gd->disk_name, "dcssblk%d",
|
||||||
MINOR(disk_devt(dev_info->gd)));
|
MINOR(disk_devt(dev_info->gd)));
|
||||||
list_add_tail(&dev_info->lh, &dcssblk_devices);
|
list_add_tail(&dev_info->lh, &dcssblk_devices);
|
||||||
|
|
||||||
if (!try_module_get(THIS_MODULE)) {
|
if (!try_module_get(THIS_MODULE)) {
|
||||||
rc = -ENODEV;
|
rc = -ENODEV;
|
||||||
goto list_del;
|
goto dev_list_del;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* register the device
|
* register the device
|
||||||
*/
|
*/
|
||||||
rc = device_register(&dev_info->dev);
|
rc = device_register(&dev_info->dev);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
PRINT_ERR("Segment %s could not be registered RC=%d\n",
|
|
||||||
local_buf, rc);
|
|
||||||
module_put(THIS_MODULE);
|
module_put(THIS_MODULE);
|
||||||
goto list_del;
|
goto dev_list_del;
|
||||||
}
|
}
|
||||||
get_device(&dev_info->dev);
|
get_device(&dev_info->dev);
|
||||||
rc = device_create_file(&dev_info->dev, &dev_attr_shared);
|
rc = device_create_file(&dev_info->dev, &dev_attr_shared);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto unregister_dev;
|
goto unregister_dev;
|
||||||
rc = device_create_file(&dev_info->dev, &dev_attr_save);
|
rc = device_create_file(&dev_info->dev, &dev_attr_save);
|
||||||
|
if (rc)
|
||||||
|
goto unregister_dev;
|
||||||
|
rc = device_create_file(&dev_info->dev, &dev_attr_seglist);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto unregister_dev;
|
goto unregister_dev;
|
||||||
|
|
||||||
|
@ -434,7 +672,6 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
|
||||||
set_disk_ro(dev_info->gd,0);
|
set_disk_ro(dev_info->gd,0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
PRINT_DEBUG("Segment %s loaded successfully\n", local_buf);
|
|
||||||
up_write(&dcssblk_devices_sem);
|
up_write(&dcssblk_devices_sem);
|
||||||
rc = count;
|
rc = count;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -445,20 +682,27 @@ unregister_dev:
|
||||||
dev_info->gd->queue = NULL;
|
dev_info->gd->queue = NULL;
|
||||||
put_disk(dev_info->gd);
|
put_disk(dev_info->gd);
|
||||||
device_unregister(&dev_info->dev);
|
device_unregister(&dev_info->dev);
|
||||||
segment_unload(dev_info->segment_name);
|
list_for_each_entry(seg_info, &dev_info->seg_list, lh) {
|
||||||
|
segment_unload(seg_info->segment_name);
|
||||||
|
}
|
||||||
put_device(&dev_info->dev);
|
put_device(&dev_info->dev);
|
||||||
up_write(&dcssblk_devices_sem);
|
up_write(&dcssblk_devices_sem);
|
||||||
goto out;
|
goto out;
|
||||||
list_del:
|
dev_list_del:
|
||||||
list_del(&dev_info->lh);
|
list_del(&dev_info->lh);
|
||||||
up_write(&dcssblk_devices_sem);
|
release_gd:
|
||||||
unload_seg:
|
|
||||||
segment_unload(local_buf);
|
|
||||||
dealloc_gendisk:
|
|
||||||
blk_cleanup_queue(dev_info->dcssblk_queue);
|
blk_cleanup_queue(dev_info->dcssblk_queue);
|
||||||
dev_info->gd->queue = NULL;
|
dev_info->gd->queue = NULL;
|
||||||
put_disk(dev_info->gd);
|
put_disk(dev_info->gd);
|
||||||
free_dev_info:
|
up_write(&dcssblk_devices_sem);
|
||||||
|
seg_list_del:
|
||||||
|
if (dev_info == NULL)
|
||||||
|
goto out;
|
||||||
|
list_for_each_entry_safe(seg_info, temp, &dev_info->seg_list, lh) {
|
||||||
|
list_del(&seg_info->lh);
|
||||||
|
segment_unload(seg_info->segment_name);
|
||||||
|
kfree(seg_info);
|
||||||
|
}
|
||||||
kfree(dev_info);
|
kfree(dev_info);
|
||||||
out:
|
out:
|
||||||
kfree(local_buf);
|
kfree(local_buf);
|
||||||
|
@ -473,6 +717,7 @@ static ssize_t
|
||||||
dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
struct dcssblk_dev_info *dev_info;
|
struct dcssblk_dev_info *dev_info;
|
||||||
|
struct segment_info *entry;
|
||||||
int rc, i;
|
int rc, i;
|
||||||
char *local_buf;
|
char *local_buf;
|
||||||
|
|
||||||
|
@ -499,26 +744,28 @@ dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const ch
|
||||||
dev_info = dcssblk_get_device_by_name(local_buf);
|
dev_info = dcssblk_get_device_by_name(local_buf);
|
||||||
if (dev_info == NULL) {
|
if (dev_info == NULL) {
|
||||||
up_write(&dcssblk_devices_sem);
|
up_write(&dcssblk_devices_sem);
|
||||||
PRINT_WARN("Segment %s is not loaded!\n", local_buf);
|
PRINT_WARN("Device %s is not loaded!\n", local_buf);
|
||||||
rc = -ENODEV;
|
rc = -ENODEV;
|
||||||
goto out_buf;
|
goto out_buf;
|
||||||
}
|
}
|
||||||
if (atomic_read(&dev_info->use_count) != 0) {
|
if (atomic_read(&dev_info->use_count) != 0) {
|
||||||
up_write(&dcssblk_devices_sem);
|
up_write(&dcssblk_devices_sem);
|
||||||
PRINT_WARN("Segment %s is in use!\n", local_buf);
|
PRINT_WARN("Device %s is in use!\n", local_buf);
|
||||||
rc = -EBUSY;
|
rc = -EBUSY;
|
||||||
goto out_buf;
|
goto out_buf;
|
||||||
}
|
}
|
||||||
list_del(&dev_info->lh);
|
|
||||||
|
|
||||||
|
list_del(&dev_info->lh);
|
||||||
del_gendisk(dev_info->gd);
|
del_gendisk(dev_info->gd);
|
||||||
blk_cleanup_queue(dev_info->dcssblk_queue);
|
blk_cleanup_queue(dev_info->dcssblk_queue);
|
||||||
dev_info->gd->queue = NULL;
|
dev_info->gd->queue = NULL;
|
||||||
put_disk(dev_info->gd);
|
put_disk(dev_info->gd);
|
||||||
device_unregister(&dev_info->dev);
|
device_unregister(&dev_info->dev);
|
||||||
segment_unload(dev_info->segment_name);
|
|
||||||
PRINT_DEBUG("Segment %s unloaded successfully\n",
|
/* unload all related segments */
|
||||||
dev_info->segment_name);
|
list_for_each_entry(entry, &dev_info->seg_list, lh)
|
||||||
|
segment_unload(entry->segment_name);
|
||||||
|
|
||||||
put_device(&dev_info->dev);
|
put_device(&dev_info->dev);
|
||||||
up_write(&dcssblk_devices_sem);
|
up_write(&dcssblk_devices_sem);
|
||||||
|
|
||||||
|
@ -550,6 +797,7 @@ static int
|
||||||
dcssblk_release(struct inode *inode, struct file *filp)
|
dcssblk_release(struct inode *inode, struct file *filp)
|
||||||
{
|
{
|
||||||
struct dcssblk_dev_info *dev_info;
|
struct dcssblk_dev_info *dev_info;
|
||||||
|
struct segment_info *entry;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
dev_info = inode->i_bdev->bd_disk->private_data;
|
dev_info = inode->i_bdev->bd_disk->private_data;
|
||||||
|
@ -560,9 +808,11 @@ dcssblk_release(struct inode *inode, struct file *filp)
|
||||||
down_write(&dcssblk_devices_sem);
|
down_write(&dcssblk_devices_sem);
|
||||||
if (atomic_dec_and_test(&dev_info->use_count)
|
if (atomic_dec_and_test(&dev_info->use_count)
|
||||||
&& (dev_info->save_pending)) {
|
&& (dev_info->save_pending)) {
|
||||||
PRINT_INFO("Segment %s became idle and is being saved now\n",
|
PRINT_INFO("Device %s became idle and is being saved now\n",
|
||||||
dev_info->segment_name);
|
dev_info->segment_name);
|
||||||
segment_save(dev_info->segment_name);
|
list_for_each_entry(entry, &dev_info->seg_list, lh) {
|
||||||
|
segment_save(entry->segment_name);
|
||||||
|
}
|
||||||
dev_info->save_pending = 0;
|
dev_info->save_pending = 0;
|
||||||
}
|
}
|
||||||
up_write(&dcssblk_devices_sem);
|
up_write(&dcssblk_devices_sem);
|
||||||
|
@ -602,7 +852,7 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio)
|
||||||
case SEG_TYPE_SC:
|
case SEG_TYPE_SC:
|
||||||
/* cannot write to these segments */
|
/* cannot write to these segments */
|
||||||
if (bio_data_dir(bio) == WRITE) {
|
if (bio_data_dir(bio) == WRITE) {
|
||||||
PRINT_WARN("rejecting write to ro segment %s\n",
|
PRINT_WARN("rejecting write to ro device %s\n",
|
||||||
dev_name(&dev_info->dev));
|
dev_name(&dev_info->dev));
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -658,7 +908,7 @@ static void
|
||||||
dcssblk_check_params(void)
|
dcssblk_check_params(void)
|
||||||
{
|
{
|
||||||
int rc, i, j, k;
|
int rc, i, j, k;
|
||||||
char buf[9];
|
char buf[DCSSBLK_PARM_LEN + 1];
|
||||||
struct dcssblk_dev_info *dev_info;
|
struct dcssblk_dev_info *dev_info;
|
||||||
|
|
||||||
for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0');
|
for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0');
|
||||||
|
@ -666,15 +916,16 @@ dcssblk_check_params(void)
|
||||||
for (j = i; (dcssblk_segments[j] != ',') &&
|
for (j = i; (dcssblk_segments[j] != ',') &&
|
||||||
(dcssblk_segments[j] != '\0') &&
|
(dcssblk_segments[j] != '\0') &&
|
||||||
(dcssblk_segments[j] != '(') &&
|
(dcssblk_segments[j] != '(') &&
|
||||||
(j - i) < 8; j++)
|
(j < DCSSBLK_PARM_LEN); j++)
|
||||||
{
|
{
|
||||||
buf[j-i] = dcssblk_segments[j];
|
buf[j-i] = dcssblk_segments[j];
|
||||||
}
|
}
|
||||||
buf[j-i] = '\0';
|
buf[j-i] = '\0';
|
||||||
rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i);
|
rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i);
|
||||||
if ((rc >= 0) && (dcssblk_segments[j] == '(')) {
|
if ((rc >= 0) && (dcssblk_segments[j] == '(')) {
|
||||||
for (k = 0; buf[k] != '\0'; k++)
|
for (k = 0; (buf[k] != ':') && (buf[k] != '\0'); k++)
|
||||||
buf[k] = toupper(buf[k]);
|
buf[k] = toupper(buf[k]);
|
||||||
|
buf[k] = '\0';
|
||||||
if (!strncmp(&dcssblk_segments[j], "(local)", 7)) {
|
if (!strncmp(&dcssblk_segments[j], "(local)", 7)) {
|
||||||
down_read(&dcssblk_devices_sem);
|
down_read(&dcssblk_devices_sem);
|
||||||
dev_info = dcssblk_get_device_by_name(buf);
|
dev_info = dcssblk_get_device_by_name(buf);
|
||||||
|
@ -741,10 +992,12 @@ module_exit(dcssblk_exit);
|
||||||
|
|
||||||
module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444);
|
module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444);
|
||||||
MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, "
|
MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, "
|
||||||
"comma-separated list, each name max. 8 chars.\n"
|
"comma-separated list, names in each set separated "
|
||||||
"Adding \"(local)\" to segment name equals echoing 0 to "
|
"by commas are separated by colons, each set contains "
|
||||||
"/sys/devices/dcssblk/<segment name>/shared after loading "
|
"names of contiguous segments and each name max. 8 chars.\n"
|
||||||
"the segment - \n"
|
"Adding \"(local)\" to the end of each set equals echoing 0 "
|
||||||
"e.g. segments=\"mydcss1,mydcss2,mydcss3(local)\"");
|
"to /sys/devices/dcssblk/<device name>/shared after loading "
|
||||||
|
"the contiguous segments - \n"
|
||||||
|
"e.g. segments=\"mydcss1,mydcss2:mydcss3,mydcss4(local)\"");
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
Загрузка…
Ссылка в новой задаче