diff options
Diffstat (limited to 'fs/exofs/ore_raid.c')
| -rw-r--r-- | fs/exofs/ore_raid.c | 75 | 
1 files changed, 55 insertions, 20 deletions
diff --git a/fs/exofs/ore_raid.c b/fs/exofs/ore_raid.c index 27cbdb697649..199590f36203 100644 --- a/fs/exofs/ore_raid.c +++ b/fs/exofs/ore_raid.c @@ -71,6 +71,11 @@ static int _sp2d_alloc(unsigned pages_in_unit, unsigned group_width,  {  	struct __stripe_pages_2d *sp2d;  	unsigned data_devs = group_width - parity; + +	/* +	 * Desired allocation layout is, though when larger than PAGE_SIZE, +	 * each struct __alloc_1p_arrays is separately allocated: +  	struct _alloc_all_bytes {  		struct __alloc_stripe_pages_2d {  			struct __stripe_pages_2d sp2d; @@ -82,55 +87,85 @@ static int _sp2d_alloc(unsigned pages_in_unit, unsigned group_width,  			char page_is_read[data_devs];  		} __a1pa[pages_in_unit];  	} *_aab; +  	struct __alloc_1p_arrays *__a1pa;  	struct __alloc_1p_arrays *__a1pa_end; -	const unsigned sizeof__a1pa = sizeof(_aab->__a1pa[0]); + +	*/ + +	char *__a1pa; +	char *__a1pa_end; + +	const size_t sizeof_stripe_pages_2d = +		sizeof(struct __stripe_pages_2d) + +		sizeof(struct __1_page_stripe) * pages_in_unit; +	const size_t sizeof__a1pa = +		ALIGN(sizeof(struct page *) * (2 * group_width) + data_devs, +		      sizeof(void *)); +	const size_t sizeof__a1pa_arrays = sizeof__a1pa * pages_in_unit; +	const size_t alloc_total = sizeof_stripe_pages_2d + +				   sizeof__a1pa_arrays; +  	unsigned num_a1pa, alloc_size, i;  	/* FIXME: check these numbers in ore_verify_layout */ -	BUG_ON(sizeof(_aab->__asp2d) > PAGE_SIZE); +	BUG_ON(sizeof_stripe_pages_2d > PAGE_SIZE);  	BUG_ON(sizeof__a1pa > PAGE_SIZE); -	if (sizeof(*_aab) > PAGE_SIZE) { -		num_a1pa = (PAGE_SIZE - sizeof(_aab->__asp2d)) / sizeof__a1pa; -		alloc_size = sizeof(_aab->__asp2d) + sizeof__a1pa * num_a1pa; +	/* +	 * If alloc_total would be larger than PAGE_SIZE, only allocate +	 * as many a1pa items as would fill the rest of the page, instead +	 * of the full pages_in_unit count. +	 */ +	if (alloc_total > PAGE_SIZE) { +		num_a1pa = (PAGE_SIZE - sizeof_stripe_pages_2d) / sizeof__a1pa; +		alloc_size = sizeof_stripe_pages_2d + sizeof__a1pa * num_a1pa;  	} else {  		num_a1pa = pages_in_unit; -		alloc_size = sizeof(*_aab); +		alloc_size = alloc_total;  	} -	_aab = kzalloc(alloc_size, GFP_KERNEL); -	if (unlikely(!_aab)) { +	*psp2d = sp2d = kzalloc(alloc_size, GFP_KERNEL); +	if (unlikely(!sp2d)) {  		ORE_DBGMSG("!! Failed to alloc sp2d size=%d\n", alloc_size);  		return -ENOMEM;  	} +	/* From here Just call _sp2d_free */ -	sp2d = &_aab->__asp2d.sp2d; -	*psp2d = sp2d; /* From here Just call _sp2d_free */ - -	__a1pa = _aab->__a1pa; -	__a1pa_end = __a1pa + num_a1pa; +	/* Find start of a1pa area. */ +	__a1pa = (char *)sp2d + sizeof_stripe_pages_2d; +	/* Find end of the _allocated_ a1pa area. */ +	__a1pa_end = __a1pa + alloc_size; +	/* Allocate additionally needed a1pa items in PAGE_SIZE chunks. */  	for (i = 0; i < pages_in_unit; ++i) { +		struct __1_page_stripe *stripe = &sp2d->_1p_stripes[i]; +  		if (unlikely(__a1pa >= __a1pa_end)) {  			num_a1pa = min_t(unsigned, PAGE_SIZE / sizeof__a1pa,  							pages_in_unit - i); +			alloc_size = sizeof__a1pa * num_a1pa; -			__a1pa = kcalloc(num_a1pa, sizeof__a1pa, GFP_KERNEL); +			__a1pa = kzalloc(alloc_size, GFP_KERNEL);  			if (unlikely(!__a1pa)) {  				ORE_DBGMSG("!! Failed to _alloc_1p_arrays=%d\n",  					   num_a1pa);  				return -ENOMEM;  			} -			__a1pa_end = __a1pa + num_a1pa; +			__a1pa_end = __a1pa + alloc_size;  			/* First *pages is marked for kfree of the buffer */ -			sp2d->_1p_stripes[i].alloc = true; +			stripe->alloc = true;  		} -		sp2d->_1p_stripes[i].pages = __a1pa->pages; -		sp2d->_1p_stripes[i].scribble = __a1pa->scribble ; -		sp2d->_1p_stripes[i].page_is_read = __a1pa->page_is_read; -		++__a1pa; +		/* +		 * Attach all _lp_stripes pointers to the allocation for +		 * it which was either part of the original PAGE_SIZE +		 * allocation or the subsequent allocation in this loop. +		 */ +		stripe->pages = (void *)__a1pa; +		stripe->scribble = stripe->pages + group_width; +		stripe->page_is_read = (char *)stripe->scribble + group_width; +		__a1pa += sizeof__a1pa;  	}  	sp2d->parity = parity;  |