[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_LOADNOLY 0x10
|
||||
#define DCSS_SEGEXT 0x18
|
||||
#define DCSS_LOADSHRX 0x20
|
||||
#define DCSS_LOADNSRX 0x24
|
||||
#define DCSS_FINDSEGX 0x2c
|
||||
#define DCSS_SEGEXTX 0x38
|
||||
#define DCSS_FINDSEGA 0x0c
|
||||
|
||||
struct qrange {
|
||||
unsigned int start; // 3byte start address, 1 byte type
|
||||
unsigned int end; // 3byte end address, 1 byte reserved
|
||||
unsigned long start; /* last byte type */
|
||||
unsigned long end; /* last byte reserved */
|
||||
};
|
||||
|
||||
struct qout64 {
|
||||
int segstart;
|
||||
int segend;
|
||||
unsigned long segstart;
|
||||
unsigned long segend;
|
||||
int segcnt;
|
||||
int segrcnt;
|
||||
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 {
|
||||
char qopcode;
|
||||
char rsrv1[3];
|
||||
|
@ -86,6 +106,55 @@ static DEFINE_MUTEX(dcss_lock);
|
|||
static LIST_HEAD(dcss_list);
|
||||
static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
|
||||
"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
|
||||
|
@ -135,25 +204,45 @@ segment_by_name (char *name)
|
|||
* Perform a function on a dcss segment.
|
||||
*/
|
||||
static inline int
|
||||
dcss_diag (__u8 func, void *parameter,
|
||||
dcss_diag(int *func, void *parameter,
|
||||
unsigned long *ret1, unsigned long *ret2)
|
||||
{
|
||||
unsigned long rx, ry;
|
||||
int rc;
|
||||
|
||||
if (scode_set == 0) {
|
||||
rc = dcss_set_subcodes();
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
scode_set = 1;
|
||||
}
|
||||
rx = (unsigned long) parameter;
|
||||
ry = (unsigned long) func;
|
||||
asm volatile(
|
||||
ry = (unsigned long) *func;
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
" sam31\n"
|
||||
" diag %0,%1,0x64\n"
|
||||
" sam64\n"
|
||||
/* 64-bit Diag x'64' new subcode, keep in 64-bit addressing mode */
|
||||
if (*func > DCSS_SEGEXT)
|
||||
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
|
||||
asm volatile(
|
||||
" diag %0,%1,0x64\n"
|
||||
#endif
|
||||
" ipm %2\n"
|
||||
" srl %2,28\n"
|
||||
: "+d" (rx), "+d" (ry), "=d" (rc) : : "cc");
|
||||
#endif
|
||||
*ret1 = rx;
|
||||
*ret2 = ry;
|
||||
return rc;
|
||||
|
@ -190,14 +279,45 @@ query_segment_type (struct dcss_segment *seg)
|
|||
qin->qoutlen = sizeof(struct qout64);
|
||||
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) {
|
||||
PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc);
|
||||
rc = dcss_diag_translate_rc (vmrc);
|
||||
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) {
|
||||
rc = -ENOTSUPP;
|
||||
goto out_free;
|
||||
|
@ -268,6 +388,30 @@ segment_type (char* name)
|
|||
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
|
||||
*/
|
||||
|
@ -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),
|
||||
GFP_DMA);
|
||||
int dcss_command, rc, diag_cc;
|
||||
int rc, diag_cc;
|
||||
unsigned long start_addr, end_addr, dummy;
|
||||
|
||||
if (seg == NULL) {
|
||||
rc = -ENOMEM;
|
||||
|
@ -287,6 +432,13 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
|
|||
if (rc < 0)
|
||||
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);
|
||||
|
||||
if (rc)
|
||||
|
@ -316,20 +468,28 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
|
|||
}
|
||||
|
||||
if (do_nonshared)
|
||||
dcss_command = DCSS_LOADNSR;
|
||||
diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name,
|
||||
&start_addr, &end_addr);
|
||||
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);
|
||||
diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name,
|
||||
&start_addr, &end_addr);
|
||||
if (diag_cc < 0) {
|
||||
dcss_diag(&purgeseg_scode, seg->dcss_name,
|
||||
&dummy, &dummy);
|
||||
rc = diag_cc;
|
||||
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;
|
||||
atomic_set(&seg->ref_count, 1);
|
||||
list_add(&seg->list, &dcss_list);
|
||||
|
@ -423,8 +583,8 @@ int
|
|||
segment_modify_shared (char *name, int do_nonshared)
|
||||
{
|
||||
struct dcss_segment *seg;
|
||||
unsigned long dummy;
|
||||
int dcss_command, rc, diag_cc;
|
||||
unsigned long start_addr, end_addr, dummy;
|
||||
int rc, diag_cc;
|
||||
|
||||
mutex_lock(&dcss_lock);
|
||||
seg = segment_by_name (name);
|
||||
|
@ -445,38 +605,51 @@ segment_modify_shared (char *name, int do_nonshared)
|
|||
goto out_unlock;
|
||||
}
|
||||
release_resource(seg->res);
|
||||
if (do_nonshared) {
|
||||
dcss_command = DCSS_LOADNSR;
|
||||
if (do_nonshared)
|
||||
seg->res->flags &= ~IORESOURCE_READONLY;
|
||||
} else {
|
||||
dcss_command = DCSS_LOADNOLY;
|
||||
else
|
||||
if (seg->vm_segtype == SEG_TYPE_SR ||
|
||||
seg->vm_segtype == SEG_TYPE_ER)
|
||||
seg->res->flags |= IORESOURCE_READONLY;
|
||||
}
|
||||
|
||||
if (request_resource(&iomem_resource, seg->res)) {
|
||||
PRINT_WARN("segment_modify_shared: could not reload segment %s"
|
||||
" - overlapping resources\n", name);
|
||||
rc = -EBUSY;
|
||||
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) {
|
||||
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;
|
||||
" - diag returned error (%ld)\n",
|
||||
name, end_addr);
|
||||
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;
|
||||
rc = 0;
|
||||
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);
|
||||
list_del(&seg->list);
|
||||
dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
|
||||
dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
|
||||
kfree(seg);
|
||||
out_unlock:
|
||||
mutex_unlock(&dcss_lock);
|
||||
|
@ -510,7 +683,7 @@ segment_unload(char *name)
|
|||
kfree(seg->res);
|
||||
vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1);
|
||||
list_del(&seg->list);
|
||||
dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy);
|
||||
dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy);
|
||||
kfree(seg);
|
||||
out_unlock:
|
||||
mutex_unlock(&dcss_lock);
|
||||
|
@ -545,7 +718,7 @@ segment_save(char *name)
|
|||
endpfn = (seg->end) >> PAGE_SHIFT;
|
||||
sprintf(cmd1, "DEFSEG %s", name);
|
||||
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].end >> PAGE_SHIFT,
|
||||
segtype_string[seg->range[i].start & 0xff]);
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " 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_release(struct inode *inode, struct file *filp);
|
||||
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,
|
||||
};
|
||||
|
||||
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 list_head lh;
|
||||
struct device dev;
|
||||
|
@ -80,8 +59,42 @@ struct dcssblk_dev_info {
|
|||
unsigned char save_pending;
|
||||
unsigned char is_shared;
|
||||
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 struct rw_semaphore dcssblk_devices_sem;
|
||||
|
||||
|
@ -91,8 +104,15 @@ static struct rw_semaphore dcssblk_devices_sem;
|
|||
static void
|
||||
dcssblk_release_segment(struct device *dev)
|
||||
{
|
||||
PRINT_DEBUG("segment release fn called for %s\n", dev_name(dev));
|
||||
kfree(container_of(dev, struct dcssblk_dev_info, dev));
|
||||
struct dcssblk_dev_info *dev_info;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -142,6 +162,169 @@ dcssblk_get_device_by_name(char *name)
|
|||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
struct dcssblk_dev_info *dev_info;
|
||||
struct segment_info *entry, *temp;
|
||||
int rc;
|
||||
|
||||
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);
|
||||
dev_info = container_of(dev, struct dcssblk_dev_info, dev);
|
||||
if (atomic_read(&dev_info->use_count)) {
|
||||
PRINT_ERR("share: segment %s is busy!\n",
|
||||
dev_info->segment_name);
|
||||
rc = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
if (inbuf[0] == '1') {
|
||||
// reload segment in shared mode
|
||||
rc = segment_modify_shared(dev_info->segment_name,
|
||||
SEGMENT_SHARED);
|
||||
if (rc < 0) {
|
||||
BUG_ON(rc == -EINVAL);
|
||||
if (rc != -EAGAIN)
|
||||
goto removeseg;
|
||||
} else {
|
||||
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);
|
||||
/* reload segments in shared mode */
|
||||
list_for_each_entry(entry, &dev_info->seg_list, lh) {
|
||||
rc = segment_modify_shared(entry->segment_name,
|
||||
SEGMENT_SHARED);
|
||||
if (rc < 0) {
|
||||
BUG_ON(rc == -EINVAL);
|
||||
if (rc != -EAGAIN)
|
||||
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);
|
||||
}
|
||||
} else if (inbuf[0] == '0') {
|
||||
// reload segment in exclusive mode
|
||||
/* reload segments 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);
|
||||
"non-shared mode\n", dev_info->segment_name);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rc = segment_modify_shared(dev_info->segment_name,
|
||||
SEGMENT_EXCLUSIVE);
|
||||
if (rc < 0) {
|
||||
BUG_ON(rc == -EINVAL);
|
||||
if (rc != -EAGAIN)
|
||||
goto removeseg;
|
||||
} else {
|
||||
dev_info->is_shared = 0;
|
||||
set_disk_ro(dev_info->gd, 0);
|
||||
list_for_each_entry(entry, &dev_info->seg_list, lh) {
|
||||
rc = segment_modify_shared(entry->segment_name,
|
||||
SEGMENT_EXCLUSIVE);
|
||||
if (rc < 0) {
|
||||
BUG_ON(rc == -EINVAL);
|
||||
if (rc != -EAGAIN)
|
||||
goto removeseg;
|
||||
}
|
||||
}
|
||||
dev_info->is_shared = 0;
|
||||
set_disk_ro(dev_info->gd, 0);
|
||||
} else {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
|
@ -220,8 +404,14 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch
|
|||
goto out;
|
||||
|
||||
removeseg:
|
||||
PRINT_ERR("Could not reload segment %s, removing it now!\n",
|
||||
dev_info->segment_name);
|
||||
PRINT_ERR("Could not reload segment(s) of the device %s, removing "
|
||||
"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);
|
||||
|
||||
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)
|
||||
{
|
||||
struct dcssblk_dev_info *dev_info;
|
||||
struct segment_info *entry;
|
||||
|
||||
if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0'))
|
||||
return -EINVAL;
|
||||
|
@ -263,14 +454,16 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char
|
|||
if (inbuf[0] == '1') {
|
||||
if (atomic_read(&dev_info->use_count) == 0) {
|
||||
// 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);
|
||||
segment_save(dev_info->segment_name);
|
||||
list_for_each_entry(entry, &dev_info->seg_list, lh) {
|
||||
segment_save(entry->segment_name);
|
||||
}
|
||||
} else {
|
||||
// device is busy => we save it when it becomes
|
||||
// idle in dcssblk_release
|
||||
PRINT_INFO("Segment %s is currently busy, it will "
|
||||
"be saved when it becomes idle...\n",
|
||||
PRINT_INFO("Device %s is currently busy, segment(s) "
|
||||
"will be saved when it becomes idle...\n",
|
||||
dev_info->segment_name);
|
||||
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
|
||||
// request
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
|
@ -290,67 +484,124 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char
|
|||
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
|
||||
*/
|
||||
static ssize_t
|
||||
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 segment_info *seg_info, *temp;
|
||||
char *local_buf;
|
||||
unsigned long seg_byte_size;
|
||||
|
||||
dev_info = NULL;
|
||||
seg_info = NULL;
|
||||
if (dev != dcssblk_root_dev) {
|
||||
rc = -EINVAL;
|
||||
goto out_nobuf;
|
||||
}
|
||||
if ((count < 1) || (buf[0] == '\0') || (buf[0] == '\n')) {
|
||||
rc = -ENAMETOOLONG;
|
||||
goto out_nobuf;
|
||||
}
|
||||
|
||||
local_buf = kmalloc(count + 1, GFP_KERNEL);
|
||||
if (local_buf == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out_nobuf;
|
||||
}
|
||||
|
||||
/*
|
||||
* parse input
|
||||
*/
|
||||
num_of_segments = 0;
|
||||
for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) {
|
||||
local_buf[i] = toupper(buf[i]);
|
||||
}
|
||||
local_buf[i] = '\0';
|
||||
if ((i == 0) || (i > 8)) {
|
||||
rc = -ENAMETOOLONG;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* already loaded?
|
||||
*/
|
||||
down_read(&dcssblk_devices_sem);
|
||||
dev_info = dcssblk_get_device_by_name(local_buf);
|
||||
up_read(&dcssblk_devices_sem);
|
||||
if (dev_info != NULL) {
|
||||
PRINT_WARN("Segment %s already loaded!\n", local_buf);
|
||||
rc = -EEXIST;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* get a struct dcssblk_dev_info
|
||||
*/
|
||||
dev_info = kzalloc(sizeof(struct dcssblk_dev_info), GFP_KERNEL);
|
||||
if (dev_info == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
for (j = i; (buf[j] != ':') &&
|
||||
(buf[j] != '\0') &&
|
||||
(buf[j] != '\n') &&
|
||||
j < count; j++) {
|
||||
local_buf[j-i] = toupper(buf[j]);
|
||||
}
|
||||
local_buf[j-i] = '\0';
|
||||
if (((j - i) == 0) || ((j - i) > 8)) {
|
||||
rc = -ENAMETOOLONG;
|
||||
goto seg_list_del;
|
||||
}
|
||||
|
||||
rc = dcssblk_load_segment(local_buf, &seg_info);
|
||||
if (rc < 0)
|
||||
goto seg_list_del;
|
||||
/*
|
||||
* get a struct dcssblk_dev_info
|
||||
*/
|
||||
if (num_of_segments == 0) {
|
||||
dev_info = kzalloc(sizeof(struct dcssblk_dev_info),
|
||||
GFP_KERNEL);
|
||||
if (dev_info == NULL) {
|
||||
rc = -ENOMEM;
|
||||
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);
|
||||
dev_set_name(&dev_info->dev, local_buf);
|
||||
/* no trailing colon at the end of the input */
|
||||
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;
|
||||
INIT_LIST_HEAD(&dev_info->lh);
|
||||
|
||||
dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK);
|
||||
if (dev_info->gd == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto free_dev_info;
|
||||
goto seg_list_del;
|
||||
}
|
||||
dev_info->gd->major = dcssblk_major;
|
||||
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;
|
||||
blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request);
|
||||
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);
|
||||
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,
|
||||
seg_byte_size, seg_byte_size >> 9);
|
||||
|
||||
dev_info->segment_type = rc;
|
||||
dev_info->save_pending = 0;
|
||||
dev_info->is_shared = 1;
|
||||
dev_info->dev.parent = dcssblk_root_dev;
|
||||
|
||||
/*
|
||||
* get minor, add to list
|
||||
*get minor, add to list
|
||||
*/
|
||||
down_write(&dcssblk_devices_sem);
|
||||
if (dcssblk_get_device_by_name(local_buf)) {
|
||||
up_write(&dcssblk_devices_sem);
|
||||
if (dcssblk_get_segment_by_name(local_buf)) {
|
||||
rc = -EEXIST;
|
||||
goto unload_seg;
|
||||
goto release_gd;
|
||||
}
|
||||
rc = dcssblk_assign_free_minor(dev_info);
|
||||
if (rc) {
|
||||
up_write(&dcssblk_devices_sem);
|
||||
PRINT_ERR("No free minor number available! "
|
||||
"Unloading segment...\n");
|
||||
goto unload_seg;
|
||||
}
|
||||
if (rc)
|
||||
goto release_gd;
|
||||
sprintf(dev_info->gd->disk_name, "dcssblk%d",
|
||||
MINOR(disk_devt(dev_info->gd)));
|
||||
list_add_tail(&dev_info->lh, &dcssblk_devices);
|
||||
|
||||
if (!try_module_get(THIS_MODULE)) {
|
||||
rc = -ENODEV;
|
||||
goto list_del;
|
||||
goto dev_list_del;
|
||||
}
|
||||
/*
|
||||
* register the device
|
||||
*/
|
||||
rc = device_register(&dev_info->dev);
|
||||
if (rc) {
|
||||
PRINT_ERR("Segment %s could not be registered RC=%d\n",
|
||||
local_buf, rc);
|
||||
module_put(THIS_MODULE);
|
||||
goto list_del;
|
||||
goto dev_list_del;
|
||||
}
|
||||
get_device(&dev_info->dev);
|
||||
rc = device_create_file(&dev_info->dev, &dev_attr_shared);
|
||||
if (rc)
|
||||
goto unregister_dev;
|
||||
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)
|
||||
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);
|
||||
break;
|
||||
}
|
||||
PRINT_DEBUG("Segment %s loaded successfully\n", local_buf);
|
||||
up_write(&dcssblk_devices_sem);
|
||||
rc = count;
|
||||
goto out;
|
||||
|
@ -445,20 +682,27 @@ unregister_dev:
|
|||
dev_info->gd->queue = NULL;
|
||||
put_disk(dev_info->gd);
|
||||
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);
|
||||
up_write(&dcssblk_devices_sem);
|
||||
goto out;
|
||||
list_del:
|
||||
dev_list_del:
|
||||
list_del(&dev_info->lh);
|
||||
up_write(&dcssblk_devices_sem);
|
||||
unload_seg:
|
||||
segment_unload(local_buf);
|
||||
dealloc_gendisk:
|
||||
release_gd:
|
||||
blk_cleanup_queue(dev_info->dcssblk_queue);
|
||||
dev_info->gd->queue = NULL;
|
||||
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);
|
||||
out:
|
||||
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)
|
||||
{
|
||||
struct dcssblk_dev_info *dev_info;
|
||||
struct segment_info *entry;
|
||||
int rc, i;
|
||||
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);
|
||||
if (dev_info == NULL) {
|
||||
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;
|
||||
goto out_buf;
|
||||
}
|
||||
if (atomic_read(&dev_info->use_count) != 0) {
|
||||
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;
|
||||
goto out_buf;
|
||||
}
|
||||
list_del(&dev_info->lh);
|
||||
|
||||
list_del(&dev_info->lh);
|
||||
del_gendisk(dev_info->gd);
|
||||
blk_cleanup_queue(dev_info->dcssblk_queue);
|
||||
dev_info->gd->queue = NULL;
|
||||
put_disk(dev_info->gd);
|
||||
device_unregister(&dev_info->dev);
|
||||
segment_unload(dev_info->segment_name);
|
||||
PRINT_DEBUG("Segment %s unloaded successfully\n",
|
||||
dev_info->segment_name);
|
||||
|
||||
/* unload all related segments */
|
||||
list_for_each_entry(entry, &dev_info->seg_list, lh)
|
||||
segment_unload(entry->segment_name);
|
||||
|
||||
put_device(&dev_info->dev);
|
||||
up_write(&dcssblk_devices_sem);
|
||||
|
||||
|
@ -550,6 +797,7 @@ static int
|
|||
dcssblk_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct dcssblk_dev_info *dev_info;
|
||||
struct segment_info *entry;
|
||||
int rc;
|
||||
|
||||
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);
|
||||
if (atomic_dec_and_test(&dev_info->use_count)
|
||||
&& (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);
|
||||
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;
|
||||
}
|
||||
up_write(&dcssblk_devices_sem);
|
||||
|
@ -602,7 +852,7 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio)
|
|||
case SEG_TYPE_SC:
|
||||
/* cannot write to these segments */
|
||||
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));
|
||||
goto fail;
|
||||
}
|
||||
|
@ -658,7 +908,7 @@ static void
|
|||
dcssblk_check_params(void)
|
||||
{
|
||||
int rc, i, j, k;
|
||||
char buf[9];
|
||||
char buf[DCSSBLK_PARM_LEN + 1];
|
||||
struct dcssblk_dev_info *dev_info;
|
||||
|
||||
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] != ',') &&
|
||||
(dcssblk_segments[j] != '\0') &&
|
||||
(dcssblk_segments[j] != '(') &&
|
||||
(j - i) < 8; j++)
|
||||
(j < DCSSBLK_PARM_LEN); j++)
|
||||
{
|
||||
buf[j-i] = dcssblk_segments[j];
|
||||
}
|
||||
buf[j-i] = '\0';
|
||||
rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i);
|
||||
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] = '\0';
|
||||
if (!strncmp(&dcssblk_segments[j], "(local)", 7)) {
|
||||
down_read(&dcssblk_devices_sem);
|
||||
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_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, "
|
||||
"comma-separated list, each name max. 8 chars.\n"
|
||||
"Adding \"(local)\" to segment name equals echoing 0 to "
|
||||
"/sys/devices/dcssblk/<segment name>/shared after loading "
|
||||
"the segment - \n"
|
||||
"e.g. segments=\"mydcss1,mydcss2,mydcss3(local)\"");
|
||||
"comma-separated list, names in each set separated "
|
||||
"by commas are separated by colons, each set contains "
|
||||
"names of contiguous segments and each name max. 8 chars.\n"
|
||||
"Adding \"(local)\" to the end of each set equals echoing 0 "
|
||||
"to /sys/devices/dcssblk/<device name>/shared after loading "
|
||||
"the contiguous segments - \n"
|
||||
"e.g. segments=\"mydcss1,mydcss2:mydcss3,mydcss4(local)\"");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
Загрузка…
Ссылка в новой задаче