[openib-general] [RFC] [PATCH] Re: CMA issue : bind selects the same port after close

Krishna Kumar krkumar2 at in.ibm.com
Tue Sep 12 02:33:04 PDT 2006


> The basic problem in the CMA is in cma_alloc_port().  If the port number
> (passed in as snum) is 0, the first available port starting at
> sysctl_local_port_range[0] is used.  We could instead start our search by
> adding an increasing counter or a random value to the lower-end of the port
> range.  Then expand the code to handle searching below our starting value 
> if we failed to find one above it.

Implement the above method where we start search for port# at a
random offset from the lower-end of the port range, and on failure
search at the lower-end of the port range.

(only compile tested)

Signed-off-by: Krishna Kumar <krkumar2 at in.ibm.com>

diff -ruNp org/core/cma.c new/core/cma.c
--- org/core/cma.c	2006-09-12 11:25:18.000000000 +0530
+++ new/core/cma.c	2006-09-12 14:28:26.000000000 +0530
@@ -1652,12 +1652,21 @@ static int cma_alloc_port(struct idr *ps
 {
 	struct rdma_bind_list *bind_list;
 	int port, start, ret;
+	int out_of_range;
 
 	bind_list = kzalloc(sizeof *bind_list, GFP_KERNEL);
 	if (!bind_list)
 		return -ENOMEM;
 
-	start = snum ? snum : sysctl_local_port_range[0];
+	if (snum) {
+		start = snum;
+	} else {
+		int low = sysctl_local_port_range[0];
+		int high = sysctl_local_port_range[1];
+
+		get_random_bytes(&start, sizeof start);
+		start = start % (high - low) + low;
+	}
 
 	do {
 		ret = idr_get_new_above(ps, bind_list, start, &port);
@@ -1666,8 +1675,21 @@ static int cma_alloc_port(struct idr *ps
 	if (ret)
 		goto err;
 
-	if ((snum && port != snum) ||
-	    (!snum && port > sysctl_local_port_range[1])) {
+	out_of_range = 0;
+	if (!snum && port > sysctl_local_port_range[1]) {
+		/*
+		 * Couldn't find one from random() off of start, try from
+		 * low.
+		 */
+		idr_remove(ps, port);
+		start = sysctl_local_port_range[0];
+		do {
+			ret = idr_get_new_above(ps, bind_list, start, &port);
+		} while ((ret == -EAGAIN) && idr_pre_get(ps, GFP_KERNEL));
+		if (port > sysctl_local_port_range[1])
+			out_of_range = 1;
+	}
+	if ((snum && port != snum) || out_of_range) {
 		idr_remove(ps, port);
 		ret = -EADDRNOTAVAIL;
 		goto err;




More information about the general mailing list