xref: /illumos-gate/usr/src/uts/common/io/ib/clients/rdsv3/cong.c (revision 1a2d662a91cee3bf82f41cd47c7ae6f3825d9db2)
1 /*
2  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /*
6  * This file contains code imported from the OFED rds source file cong.c
7  * Oracle elects to have and use the contents of cong.c under and governed
8  * by the OpenIB.org BSD license (see below for full license text). However,
9  * the following notice accompanied the original version of this file:
10  */
11 
12 
13 /*
14  * Copyright (c) 2007 Oracle.  All rights reserved.
15  *
16  * This software is available to you under a choice of one of two
17  * licenses.  You may choose to be licensed under the terms of the GNU
18  * General Public License (GPL) Version 2, available from the file
19  * COPYING in the main directory of this source tree, or the
20  * OpenIB.org BSD license below:
21  *
22  *     Redistribution and use in source and binary forms, with or
23  *     without modification, are permitted provided that the following
24  *     conditions are met:
25  *
26  *      - Redistributions of source code must retain the above
27  *        copyright notice, this list of conditions and the following
28  *        disclaimer.
29  *
30  *      - Redistributions in binary form must reproduce the above
31  *        copyright notice, this list of conditions and the following
32  *        disclaimer in the documentation and/or other materials
33  *        provided with the distribution.
34  *
35  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
36  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
37  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
38  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
39  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
40  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
41  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
42  * SOFTWARE.
43  *
44  */
45 #include <sys/rds.h>
46 
47 #include <sys/ib/clients/rdsv3/rdsv3.h>
48 #include <sys/ib/clients/rdsv3/rdsv3_impl.h>
49 #include <sys/ib/clients/rdsv3/rdsv3_debug.h>
50 
51 /*
52  * This file implements the receive side of the unconventional congestion
53  * management in RDS.
54  *
55  * Messages waiting in the receive queue on the receiving socket are accounted
56  * against the sockets SO_RCVBUF option value.  Only the payload bytes in the
57  * message are accounted for.  If the number of bytes queued equals or exceeds
58  * rcvbuf then the socket is congested.  All sends attempted to this socket's
59  * address should return block or return -EWOULDBLOCK.
60  *
61  * Applications are expected to be reasonably tuned such that this situation
62  * very rarely occurs.  An application encountering this "back-pressure" is
63  * considered a bug.
64  *
65  * This is implemented by having each node maintain bitmaps which indicate
66  * which ports on bound addresses are congested.  As the bitmap changes it is
67  * sent through all the connections which terminate in the local address of the
68  * bitmap which changed.
69  *
70  * The bitmaps are allocated as connections are brought up.  This avoids
71  * allocation in the interrupt handling path which queues messages on sockets.
72  * The dense bitmaps let transports send the entire bitmap on any bitmap change
73  * reasonably efficiently.  This is much easier to implement than some
74  * finer-grained communication of per-port congestion.  The sender does a very
75  * inexpensive bit test to test if the port it's about to send to is congested
76  * or not.
77  */
78 
79 /*
80  * Interaction with poll is a tad tricky. We want all processes stuck in
81  * poll to wake up and check whether a congested destination became uncongested.
82  * The really sad thing is we have no idea which destinations the application
83  * wants to send to - we don't even know which rdsv3_connections are involved.
84  * So until we implement a more flexible rds poll interface, we have to make
85  * do with this:
86  * We maintain a global counter that is incremented each time a congestion map
87  * update is received. Each rds socket tracks this value, and if rdsv3_poll
88  * finds that the saved generation number is smaller than the global generation
89  * number, it wakes up the process.
90  */
91 static atomic_t		rdsv3_cong_generation = ATOMIC_INIT(0);
92 
93 /*
94  * Congestion monitoring
95  */
96 static struct list rdsv3_cong_monitor;
97 static krwlock_t rdsv3_cong_monitor_lock;
98 
99 /*
100  * Yes, a global lock.  It's used so infrequently that it's worth keeping it
101  * global to simplify the locking.  It's only used in the following
102  * circumstances:
103  *
104  *  - on connection buildup to associate a conn with its maps
105  *  - on map changes to inform conns of a new map to send
106  *
107  *  It's sadly ordered under the socket callback lock and the connection lock.
108  *  Receive paths can mark ports congested from interrupt context so the
109  *  lock masks interrupts.
110  */
111 static kmutex_t rdsv3_cong_lock;
112 static struct avl_tree rdsv3_cong_tree;
113 
114 static struct rdsv3_cong_map *
115 rdsv3_cong_tree_walk(uint32_be_t addr, struct rdsv3_cong_map *insert)
116 {
117 	struct rdsv3_cong_map *map;
118 	avl_index_t where;
119 
120 	if (insert) {
121 		map = avl_find(&rdsv3_cong_tree, insert, &where);
122 		if (map == NULL) {
123 			avl_insert(&rdsv3_cong_tree, insert, where);
124 			return (NULL);
125 		}
126 	} else {
127 		struct rdsv3_cong_map map1;
128 		map1.m_addr = addr;
129 		map = avl_find(&rdsv3_cong_tree, &map1, &where);
130 	}
131 
132 	return (map);
133 }
134 
135 /*
136  * There is only ever one bitmap for any address.  Connections try and allocate
137  * these bitmaps in the process getting pointers to them.  The bitmaps are only
138  * ever freed as the module is removed after all connections have been freed.
139  */
140 static struct rdsv3_cong_map *
141 rdsv3_cong_from_addr(uint32_be_t addr)
142 {
143 	struct rdsv3_cong_map *map;
144 	struct rdsv3_cong_map *ret = NULL;
145 	unsigned long zp;
146 	unsigned long i;
147 
148 	RDSV3_DPRINTF4("rdsv3_cong_from_addr", "Enter(addr: %x)", ntohl(addr));
149 
150 	map = kmem_zalloc(sizeof (struct rdsv3_cong_map), KM_NOSLEEP);
151 	if (!map)
152 		return (NULL);
153 
154 	map->m_addr = addr;
155 	rdsv3_init_waitqueue(&map->m_waitq);
156 	list_create(&map->m_conn_list, sizeof (struct rdsv3_connection),
157 	    offsetof(struct rdsv3_connection, c_map_item));
158 
159 	for (i = 0; i < RDSV3_CONG_MAP_PAGES; i++) {
160 		zp = (unsigned long)kmem_zalloc(PAGE_SIZE, KM_NOSLEEP);
161 		if (zp == 0)
162 			goto out;
163 		map->m_page_addrs[i] = zp;
164 	}
165 
166 	mutex_enter(&rdsv3_cong_lock);
167 	ret = rdsv3_cong_tree_walk(addr, map);
168 	mutex_exit(&rdsv3_cong_lock);
169 
170 	if (!ret) {
171 		ret = map;
172 		map = NULL;
173 	}
174 
175 out:
176 	if (map) {
177 		for (i = 0; i < RDSV3_CONG_MAP_PAGES && map->m_page_addrs[i];
178 		    i++)
179 			kmem_free((void *)map->m_page_addrs[i], PAGE_SIZE);
180 		kmem_free(map, sizeof (*map));
181 	}
182 
183 	RDSV3_DPRINTF5("rdsv3_cong_from_addr", "map %p for addr %x",
184 	    ret, ntohl(addr));
185 
186 	return (ret);
187 }
188 
189 /*
190  * Put the conn on its local map's list.  This is called when the conn is
191  * really added to the hash.  It's nested under the rdsv3_conn_lock, sadly.
192  */
193 void
194 rdsv3_cong_add_conn(struct rdsv3_connection *conn)
195 {
196 	RDSV3_DPRINTF4("rdsv3_cong_add_conn", "Enter(conn: %p)", conn);
197 
198 	RDSV3_DPRINTF5("rdsv3_cong_add_conn", "conn %p now on map %p",
199 	    conn, conn->c_lcong);
200 	mutex_enter(&rdsv3_cong_lock);
201 	list_insert_tail(&conn->c_lcong->m_conn_list, conn);
202 	mutex_exit(&rdsv3_cong_lock);
203 
204 	RDSV3_DPRINTF4("rdsv3_cong_add_conn", "Return(conn: %p)", conn);
205 }
206 
207 void
208 rdsv3_cong_remove_conn(struct rdsv3_connection *conn)
209 {
210 	RDSV3_DPRINTF4("rdsv3_cong_remove_conn", "Enter(conn: %p)", conn);
211 
212 	RDSV3_DPRINTF5("rdsv3_cong_remove_conn", "removing conn %p from map %p",
213 	    conn, conn->c_lcong);
214 	mutex_enter(&rdsv3_cong_lock);
215 	list_remove_node(&conn->c_map_item);
216 	mutex_exit(&rdsv3_cong_lock);
217 
218 	RDSV3_DPRINTF4("rdsv3_cong_remove_conn", "Return(conn: %p)", conn);
219 }
220 
221 int
222 rdsv3_cong_get_maps(struct rdsv3_connection *conn)
223 {
224 	conn->c_lcong = rdsv3_cong_from_addr(conn->c_laddr);
225 	conn->c_fcong = rdsv3_cong_from_addr(conn->c_faddr);
226 
227 	if (!(conn->c_lcong && conn->c_fcong))
228 		return (-ENOMEM);
229 
230 	return (0);
231 }
232 
233 void
234 rdsv3_cong_queue_updates(struct rdsv3_cong_map *map)
235 {
236 	struct rdsv3_connection *conn;
237 
238 	RDSV3_DPRINTF4("rdsv3_cong_queue_updates", "Enter(map: %p)", map);
239 
240 	mutex_enter(&rdsv3_cong_lock);
241 
242 	RDSV3_FOR_EACH_LIST_NODE(conn, &map->m_conn_list, c_map_item) {
243 		if (!test_and_set_bit(0, &conn->c_map_queued)) {
244 			rdsv3_stats_inc(s_cong_update_queued);
245 			(void) rdsv3_send_xmit(conn);
246 		}
247 	}
248 
249 	mutex_exit(&rdsv3_cong_lock);
250 
251 	RDSV3_DPRINTF4("rdsv3_cong_queue_updates", "Return(map: %p)", map);
252 }
253 
254 void
255 rdsv3_cong_map_updated(struct rdsv3_cong_map *map, uint64_t portmask)
256 {
257 	RDSV3_DPRINTF4("rdsv3_cong_map_updated",
258 	    "waking map %p for %u.%u.%u.%u",
259 	    map, NIPQUAD(map->m_addr));
260 
261 	rdsv3_stats_inc(s_cong_update_received);
262 	atomic_inc_32(&rdsv3_cong_generation);
263 #if 0
264 XXX
265 	if (waitqueue_active(&map->m_waitq))
266 #endif
267 		rdsv3_wake_up(&map->m_waitq);
268 
269 	if (portmask && !list_is_empty(&rdsv3_cong_monitor)) {
270 		struct rdsv3_sock *rs;
271 
272 		rw_enter(&rdsv3_cong_monitor_lock, RW_READER);
273 		RDSV3_FOR_EACH_LIST_NODE(rs, &rdsv3_cong_monitor,
274 		    rs_cong_list) {
275 			mutex_enter(&rs->rs_lock);
276 			rs->rs_cong_notify |= (rs->rs_cong_mask & portmask);
277 			rs->rs_cong_mask &= ~portmask;
278 			mutex_exit(&rs->rs_lock);
279 			if (rs->rs_cong_notify)
280 				rdsv3_wake_sk_sleep(rs);
281 		}
282 		rw_exit(&rdsv3_cong_monitor_lock);
283 	}
284 
285 	RDSV3_DPRINTF4("rdsv3_cong_map_updated", "Return(map: %p)", map);
286 }
287 
288 int
289 rdsv3_cong_updated_since(unsigned long *recent)
290 {
291 	unsigned long gen = atomic_get(&rdsv3_cong_generation);
292 
293 	if (*recent == gen)
294 		return (0);
295 	*recent = gen;
296 	return (1);
297 }
298 
299 /*
300  * We're called under the locking that protects the sockets receive buffer
301  * consumption.  This makes it a lot easier for the caller to only call us
302  * when it knows that an existing set bit needs to be cleared, and vice versa.
303  * We can't block and we need to deal with concurrent sockets working against
304  * the same per-address map.
305  */
306 void
307 rdsv3_cong_set_bit(struct rdsv3_cong_map *map, uint16_be_t port)
308 {
309 	unsigned long i;
310 	unsigned long off;
311 
312 	RDSV3_DPRINTF4("rdsv3_cong_set_bit",
313 	    "setting congestion for %u.%u.%u.%u:%u in map %p",
314 	    NIPQUAD(map->m_addr), ntohs(port), map);
315 
316 	i = ntohs(port) / RDSV3_CONG_MAP_PAGE_BITS;
317 	off = ntohs(port) % RDSV3_CONG_MAP_PAGE_BITS;
318 	set_le_bit(off, (void *)map->m_page_addrs[i]);
319 }
320 
321 void
322 rdsv3_cong_clear_bit(struct rdsv3_cong_map *map, uint16_be_t port)
323 {
324 	unsigned long i;
325 	unsigned long off;
326 
327 	RDSV3_DPRINTF4("rdsv3_cong_clear_bit",
328 	    "clearing congestion for %u.%u.%u.%u:%u in map %p\n",
329 	    NIPQUAD(map->m_addr), ntohs(port), map);
330 
331 	i = ntohs(port) / RDSV3_CONG_MAP_PAGE_BITS;
332 	off = ntohs(port) % RDSV3_CONG_MAP_PAGE_BITS;
333 	clear_le_bit(off, (void *)map->m_page_addrs[i]);
334 }
335 
336 static int
337 rdsv3_cong_test_bit(struct rdsv3_cong_map *map, uint16_be_t port)
338 {
339 	unsigned long i;
340 	unsigned long off;
341 
342 	i = ntohs(port) / RDSV3_CONG_MAP_PAGE_BITS;
343 	off = ntohs(port) % RDSV3_CONG_MAP_PAGE_BITS;
344 
345 	RDSV3_DPRINTF5("rdsv3_cong_test_bit", "port: 0x%x i = %lx off = %lx",
346 	    ntohs(port), i, off);
347 
348 	return (test_le_bit(off, (void *)map->m_page_addrs[i]));
349 }
350 
351 void
352 rdsv3_cong_add_socket(struct rdsv3_sock *rs)
353 {
354 	RDSV3_DPRINTF4("rdsv3_cong_add_socket", "Enter(rs: %p)", rs);
355 
356 	rw_enter(&rdsv3_cong_monitor_lock, RW_WRITER);
357 	if (!list_link_active(&rs->rs_cong_list))
358 		list_insert_head(&rdsv3_cong_monitor, rs);
359 	rw_exit(&rdsv3_cong_monitor_lock);
360 }
361 
362 void
363 rdsv3_cong_remove_socket(struct rdsv3_sock *rs)
364 {
365 	struct rdsv3_cong_map *map;
366 
367 	RDSV3_DPRINTF4("rdsv3_cong_remove_socket", "Enter(rs: %p)", rs);
368 
369 	rw_enter(&rdsv3_cong_monitor_lock, RW_WRITER);
370 	list_remove_node(&rs->rs_cong_list);
371 	rw_exit(&rdsv3_cong_monitor_lock);
372 
373 	/* update congestion map for now-closed port */
374 	mutex_enter(&rdsv3_cong_lock);
375 	map = rdsv3_cong_tree_walk(rs->rs_bound_addr, NULL);
376 	mutex_exit(&rdsv3_cong_lock);
377 
378 	if (map && rdsv3_cong_test_bit(map, rs->rs_bound_port)) {
379 		rdsv3_cong_clear_bit(map, rs->rs_bound_port);
380 		rdsv3_cong_queue_updates(map);
381 	}
382 }
383 
384 int
385 rdsv3_cong_wait(struct rdsv3_cong_map *map, uint16_be_t port, int nonblock,
386     struct rdsv3_sock *rs)
387 {
388 	int ret = 0;
389 
390 	RDSV3_DPRINTF4("rdsv3_cong_wait", "Enter(rs: %p, mode: %d)",
391 	    rs, nonblock);
392 
393 	if (!rdsv3_cong_test_bit(map, port))
394 		return (0);
395 	if (nonblock) {
396 		if (rs && rs->rs_cong_monitor) {
397 			/*
398 			 * It would have been nice to have an atomic set_bit on
399 			 * a uint64_t.
400 			 */
401 			mutex_enter(&rs->rs_lock);
402 			rs->rs_cong_mask |=
403 			    RDS_CONG_MONITOR_MASK(ntohs(port));
404 			mutex_exit(&rs->rs_lock);
405 
406 			/*
407 			 * Test again - a congestion update may have arrived in
408 			 * the meantime.
409 			 */
410 			if (!rdsv3_cong_test_bit(map, port))
411 				return (0);
412 		}
413 		rdsv3_stats_inc(s_cong_send_error);
414 		return (-ENOBUFS);
415 	}
416 
417 	rdsv3_stats_inc(s_cong_send_blocked);
418 	RDSV3_DPRINTF3("rdsv3_cong_wait", "waiting on map %p for port %u",
419 	    map, ntohs(port));
420 
421 #if 0
422 	ret = rdsv3_wait_sig(&map->m_waitq, !rdsv3_cong_test_bit(map, port));
423 	if (ret == 0)
424 		return (-ERESTART);
425 	return (0);
426 #else
427 	mutex_enter(&map->m_waitq.waitq_mutex);
428 	map->m_waitq.waitq_waiters++;
429 	while (rdsv3_cong_test_bit(map, port)) {
430 		ret = cv_wait_sig(&map->m_waitq.waitq_cv,
431 		    &map->m_waitq.waitq_mutex);
432 		if (ret == 0) {
433 			ret = -EINTR;
434 			break;
435 		}
436 	}
437 	map->m_waitq.waitq_waiters--;
438 	mutex_exit(&map->m_waitq.waitq_mutex);
439 	return (ret);
440 #endif
441 }
442 
443 void
444 rdsv3_cong_exit(void)
445 {
446 	struct rdsv3_cong_map *map;
447 	unsigned long i;
448 
449 	RDSV3_DPRINTF4("rdsv3_cong_exit", "Enter");
450 
451 	while ((map = avl_first(&rdsv3_cong_tree))) {
452 		RDSV3_DPRINTF5("rdsv3_cong_exit", "freeing map %p\n", map);
453 		avl_remove(&rdsv3_cong_tree, map);
454 		for (i = 0; i < RDSV3_CONG_MAP_PAGES && map->m_page_addrs[i];
455 		    i++)
456 			kmem_free((void *)map->m_page_addrs[i], PAGE_SIZE);
457 		kmem_free(map, sizeof (*map));
458 	}
459 
460 	RDSV3_DPRINTF4("rdsv3_cong_exit", "Return");
461 }
462 
463 /*
464  * Allocate a RDS message containing a congestion update.
465  */
466 struct rdsv3_message *
467 rdsv3_cong_update_alloc(struct rdsv3_connection *conn)
468 {
469 	struct rdsv3_cong_map *map = conn->c_lcong;
470 	struct rdsv3_message *rm;
471 
472 	rm = rdsv3_message_map_pages(map->m_page_addrs, RDSV3_CONG_MAP_BYTES);
473 	if (!IS_ERR(rm))
474 		rm->m_inc.i_hdr.h_flags = RDSV3_FLAG_CONG_BITMAP;
475 
476 	return (rm);
477 }
478 
479 static int
480 rdsv3_cong_compare(const void *map1, const void *map2)
481 {
482 #define	addr1	((struct rdsv3_cong_map *)map1)->m_addr
483 #define	addr2	((struct rdsv3_cong_map *)map2)->m_addr
484 
485 	if (addr1 < addr2)
486 		return (-1);
487 	if (addr1 > addr2)
488 		return (1);
489 	return (0);
490 }
491 
492 void
493 rdsv3_cong_init(void)
494 {
495 	list_create(&rdsv3_cong_monitor, sizeof (struct rdsv3_sock),
496 	    offsetof(struct rdsv3_sock, rs_cong_list));
497 	rw_init(&rdsv3_cong_monitor_lock, NULL, RW_DRIVER, NULL);
498 	mutex_init(&rdsv3_cong_lock, NULL, MUTEX_DRIVER, NULL);
499 	avl_create(&rdsv3_cong_tree, rdsv3_cong_compare,
500 	    sizeof (struct rdsv3_cong_map), offsetof(struct rdsv3_cong_map,
501 	    m_rb_node));
502 }
503