forked from mirrors/linux
		
	[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:
		
							parent
							
								
									753c4dd6a2
								
							
						
					
					
						commit
						b2300b9efe
					
				
					 2 changed files with 605 additions and 179 deletions
				
			
		| 
						 | 
					@ -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 */
 | 
				
			||||||
 | 
						if (*func > DCSS_SEGEXT)
 | 
				
			||||||
 | 
							asm volatile(
 | 
				
			||||||
			"	diag	%0,%1,0x64\n"
 | 
								"	diag	%0,%1,0x64\n"
 | 
				
			||||||
		"	sam64\n"
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
		"	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");
 | 
				
			||||||
 | 
						/* 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"
 | 
				
			||||||
 | 
							"	ipm	%2\n"
 | 
				
			||||||
 | 
							"	srl	%2,28\n"
 | 
				
			||||||
 | 
							: "+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) {
 | 
				
			||||||
 | 
								rc = segment_modify_shared(entry->segment_name,
 | 
				
			||||||
						SEGMENT_SHARED);
 | 
											SEGMENT_SHARED);
 | 
				
			||||||
			if (rc < 0) {
 | 
								if (rc < 0) {
 | 
				
			||||||
				BUG_ON(rc == -EINVAL);
 | 
									BUG_ON(rc == -EINVAL);
 | 
				
			||||||
				if (rc != -EAGAIN)
 | 
									if (rc != -EAGAIN)
 | 
				
			||||||
					goto removeseg;
 | 
										goto removeseg;
 | 
				
			||||||
		} else {
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		dev_info->is_shared = 1;
 | 
							dev_info->is_shared = 1;
 | 
				
			||||||
		switch (dev_info->segment_type) {
 | 
							switch (dev_info->segment_type) {
 | 
				
			||||||
		case SEG_TYPE_SR:
 | 
							case SEG_TYPE_SR:
 | 
				
			||||||
		case SEG_TYPE_ER:
 | 
							case SEG_TYPE_ER:
 | 
				
			||||||
		case SEG_TYPE_SC:
 | 
							case SEG_TYPE_SC:
 | 
				
			||||||
					set_disk_ro(dev_info->gd,1);
 | 
								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) {
 | 
				
			||||||
 | 
								rc = segment_modify_shared(entry->segment_name,
 | 
				
			||||||
						   SEGMENT_EXCLUSIVE);
 | 
											   SEGMENT_EXCLUSIVE);
 | 
				
			||||||
			if (rc < 0) {
 | 
								if (rc < 0) {
 | 
				
			||||||
				BUG_ON(rc == -EINVAL);
 | 
									BUG_ON(rc == -EINVAL);
 | 
				
			||||||
				if (rc != -EAGAIN)
 | 
									if (rc != -EAGAIN)
 | 
				
			||||||
					goto removeseg;
 | 
										goto removeseg;
 | 
				
			||||||
		} else {
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		dev_info->is_shared = 0;
 | 
							dev_info->is_shared = 0;
 | 
				
			||||||
		set_disk_ro(dev_info->gd, 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 "
 | 
				
			||||||
 | 
							"segment(s) now!\n",
 | 
				
			||||||
		dev_info->segment_name);
 | 
							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') &&
 | 
				
			||||||
 | 
								(buf[j] != '\n') &&
 | 
				
			||||||
 | 
								j < count; j++) {
 | 
				
			||||||
 | 
								local_buf[j-i] = toupper(buf[j]);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	local_buf[i] = '\0';
 | 
							local_buf[j-i] = '\0';
 | 
				
			||||||
	if ((i == 0) || (i > 8)) {
 | 
							if (((j - i) == 0) || ((j - i) > 8)) {
 | 
				
			||||||
			rc = -ENAMETOOLONG;
 | 
								rc = -ENAMETOOLONG;
 | 
				
			||||||
		goto out;
 | 
								goto seg_list_del;
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 * 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;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rc = dcssblk_load_segment(local_buf, &seg_info);
 | 
				
			||||||
 | 
							if (rc < 0)
 | 
				
			||||||
 | 
								goto seg_list_del;
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * get a struct dcssblk_dev_info
 | 
							 * get a struct dcssblk_dev_info
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
	dev_info = kzalloc(sizeof(struct dcssblk_dev_info), GFP_KERNEL);
 | 
							if (num_of_segments == 0) {
 | 
				
			||||||
 | 
								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);
 | 
								strcpy(dev_info->segment_name, local_buf);
 | 
				
			||||||
	dev_set_name(&dev_info->dev, 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;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* 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;
 | 
						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 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
 | 
				
			||||||
	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");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue