diff options
| author | Jiri Kosina <jkosina@suse.cz> | 2010-12-22 18:57:02 +0100 | 
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2010-12-22 18:57:02 +0100 | 
| commit | 4b7bd364700d9ac8372eff48832062b936d0793b (patch) | |
| tree | 0dbf78c95456a0b02d07fcd473281f04a87e266d /net/rds/rdma.c | |
| parent | c0d8768af260e2cbb4bf659ae6094a262c86b085 (diff) | |
| parent | 90a8a73c06cc32b609a880d48449d7083327e11a (diff) | |
| download | olio-linux-3.10-4b7bd364700d9ac8372eff48832062b936d0793b.tar.xz olio-linux-3.10-4b7bd364700d9ac8372eff48832062b936d0793b.zip  | |
Merge branch 'master' into for-next
Conflicts:
	MAINTAINERS
	arch/arm/mach-omap2/pm24xx.c
	drivers/scsi/bfa/bfa_fcpim.c
Needed to update to apply fixes for which the old branch was too
outdated.
Diffstat (limited to 'net/rds/rdma.c')
| -rw-r--r-- | net/rds/rdma.c | 128 | 
1 files changed, 85 insertions, 43 deletions
diff --git a/net/rds/rdma.c b/net/rds/rdma.c index 1a41debca1c..4e37c1cbe8b 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -479,13 +479,38 @@ void rds_atomic_free_op(struct rm_atomic_op *ao)  /* - * Count the number of pages needed to describe an incoming iovec. + * Count the number of pages needed to describe an incoming iovec array.   */ -static int rds_rdma_pages(struct rds_rdma_args *args) +static int rds_rdma_pages(struct rds_iovec iov[], int nr_iovecs) +{ +	int tot_pages = 0; +	unsigned int nr_pages; +	unsigned int i; + +	/* figure out the number of pages in the vector */ +	for (i = 0; i < nr_iovecs; i++) { +		nr_pages = rds_pages_in_vec(&iov[i]); +		if (nr_pages == 0) +			return -EINVAL; + +		tot_pages += nr_pages; + +		/* +		 * nr_pages for one entry is limited to (UINT_MAX>>PAGE_SHIFT)+1, +		 * so tot_pages cannot overflow without first going negative. +		 */ +		if (tot_pages < 0) +			return -EINVAL; +	} + +	return tot_pages; +} + +int rds_rdma_extra_size(struct rds_rdma_args *args)  {  	struct rds_iovec vec;  	struct rds_iovec __user *local_vec; -	unsigned int tot_pages = 0; +	int tot_pages = 0;  	unsigned int nr_pages;  	unsigned int i; @@ -502,14 +527,16 @@ static int rds_rdma_pages(struct rds_rdma_args *args)  			return -EINVAL;  		tot_pages += nr_pages; -	} -	return tot_pages; -} +		/* +		 * nr_pages for one entry is limited to (UINT_MAX>>PAGE_SHIFT)+1, +		 * so tot_pages cannot overflow without first going negative. +		 */ +		if (tot_pages < 0) +			return -EINVAL; +	} -int rds_rdma_extra_size(struct rds_rdma_args *args) -{ -	return rds_rdma_pages(args) * sizeof(struct scatterlist); +	return tot_pages * sizeof(struct scatterlist);  }  /* @@ -520,13 +547,12 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,  			  struct cmsghdr *cmsg)  {  	struct rds_rdma_args *args; -	struct rds_iovec vec;  	struct rm_rdma_op *op = &rm->rdma;  	int nr_pages;  	unsigned int nr_bytes;  	struct page **pages = NULL; -	struct rds_iovec __user *local_vec; -	unsigned int nr; +	struct rds_iovec iovstack[UIO_FASTIOV], *iovs = iovstack; +	int iov_size;  	unsigned int i, j;  	int ret = 0; @@ -541,14 +567,31 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,  		goto out;  	} -	if (args->nr_local > (u64)UINT_MAX) { +	if (args->nr_local > UIO_MAXIOV) {  		ret = -EMSGSIZE;  		goto out;  	} -	nr_pages = rds_rdma_pages(args); -	if (nr_pages < 0) +	/* Check whether to allocate the iovec area */ +	iov_size = args->nr_local * sizeof(struct rds_iovec); +	if (args->nr_local > UIO_FASTIOV) { +		iovs = sock_kmalloc(rds_rs_to_sk(rs), iov_size, GFP_KERNEL); +		if (!iovs) { +			ret = -ENOMEM; +			goto out; +		} +	} + +	if (copy_from_user(iovs, (struct rds_iovec __user *)(unsigned long) args->local_vec_addr, iov_size)) { +		ret = -EFAULT; +		goto out; +	} + +	nr_pages = rds_rdma_pages(iovs, args->nr_local); +	if (nr_pages < 0) { +		ret = -EINVAL;  		goto out; +	}  	pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL);  	if (!pages) { @@ -564,6 +607,10 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,  	op->op_recverr = rs->rs_recverr;  	WARN_ON(!nr_pages);  	op->op_sg = rds_message_alloc_sgs(rm, nr_pages); +	if (!op->op_sg) { +		ret = -ENOMEM; +		goto out; +	}  	if (op->op_notify || op->op_recverr) {  		/* We allocate an uninitialized notifier here, because @@ -597,50 +644,40 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,  	       (unsigned long long)args->remote_vec.addr,  	       op->op_rkey); -	local_vec = (struct rds_iovec __user *)(unsigned long) args->local_vec_addr; -  	for (i = 0; i < args->nr_local; i++) { -		if (copy_from_user(&vec, &local_vec[i], -				   sizeof(struct rds_iovec))) { -			ret = -EFAULT; -			goto out; -		} - -		nr = rds_pages_in_vec(&vec); -		if (nr == 0) { -			ret = -EINVAL; -			goto out; -		} +		struct rds_iovec *iov = &iovs[i]; +		/* don't need to check, rds_rdma_pages() verified nr will be +nonzero */ +		unsigned int nr = rds_pages_in_vec(iov); -		rs->rs_user_addr = vec.addr; -		rs->rs_user_bytes = vec.bytes; +		rs->rs_user_addr = iov->addr; +		rs->rs_user_bytes = iov->bytes;  		/* If it's a WRITE operation, we want to pin the pages for reading.  		 * If it's a READ operation, we need to pin the pages for writing.  		 */ -		ret = rds_pin_pages(vec.addr, nr, pages, !op->op_write); +		ret = rds_pin_pages(iov->addr, nr, pages, !op->op_write);  		if (ret < 0)  			goto out; -		rdsdebug("RDS: nr_bytes %u nr %u vec.bytes %llu vec.addr %llx\n", -		       nr_bytes, nr, vec.bytes, vec.addr); +		rdsdebug("RDS: nr_bytes %u nr %u iov->bytes %llu iov->addr %llx\n", +			 nr_bytes, nr, iov->bytes, iov->addr); -		nr_bytes += vec.bytes; +		nr_bytes += iov->bytes;  		for (j = 0; j < nr; j++) { -			unsigned int offset = vec.addr & ~PAGE_MASK; +			unsigned int offset = iov->addr & ~PAGE_MASK;  			struct scatterlist *sg;  			sg = &op->op_sg[op->op_nents + j];  			sg_set_page(sg, pages[j], -					min_t(unsigned int, vec.bytes, PAGE_SIZE - offset), +					min_t(unsigned int, iov->bytes, PAGE_SIZE - offset),  					offset); -			rdsdebug("RDS: sg->offset %x sg->len %x vec.addr %llx vec.bytes %llu\n", -			       sg->offset, sg->length, vec.addr, vec.bytes); +			rdsdebug("RDS: sg->offset %x sg->len %x iov->addr %llx iov->bytes %llu\n", +			       sg->offset, sg->length, iov->addr, iov->bytes); -			vec.addr += sg->length; -			vec.bytes -= sg->length; +			iov->addr += sg->length; +			iov->bytes -= sg->length;  		}  		op->op_nents += nr; @@ -655,13 +692,14 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,  	}  	op->op_bytes = nr_bytes; -	ret = 0;  out: +	if (iovs != iovstack) +		sock_kfree_s(rds_rs_to_sk(rs), iovs, iov_size);  	kfree(pages);  	if (ret)  		rds_rdma_free_op(op); - -	rds_stats_inc(s_send_rdma); +	else +		rds_stats_inc(s_send_rdma);  	return ret;  } @@ -773,6 +811,10 @@ int rds_cmsg_atomic(struct rds_sock *rs, struct rds_message *rm,  	rm->atomic.op_active = 1;  	rm->atomic.op_recverr = rs->rs_recverr;  	rm->atomic.op_sg = rds_message_alloc_sgs(rm, 1); +	if (!rm->atomic.op_sg) { +		ret = -ENOMEM; +		goto err; +	}  	/* verify 8 byte-aligned */  	if (args->local_addr & 0x7) {  |