| Andy Grover | 922cb17 | 2009-02-24 15:30:20 +0000 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (c) 2007 Oracle.  All rights reserved. | 
|  | 3 | * | 
|  | 4 | * This software is available to you under a choice of one of two | 
|  | 5 | * licenses.  You may choose to be licensed under the terms of the GNU | 
|  | 6 | * General Public License (GPL) Version 2, available from the file | 
|  | 7 | * COPYING in the main directory of this source tree, or the | 
|  | 8 | * OpenIB.org BSD license below: | 
|  | 9 | * | 
|  | 10 | *     Redistribution and use in source and binary forms, with or | 
|  | 11 | *     without modification, are permitted provided that the following | 
|  | 12 | *     conditions are met: | 
|  | 13 | * | 
|  | 14 | *      - Redistributions of source code must retain the above | 
|  | 15 | *        copyright notice, this list of conditions and the following | 
|  | 16 | *        disclaimer. | 
|  | 17 | * | 
|  | 18 | *      - Redistributions in binary form must reproduce the above | 
|  | 19 | *        copyright notice, this list of conditions and the following | 
|  | 20 | *        disclaimer in the documentation and/or other materials | 
|  | 21 | *        provided with the distribution. | 
|  | 22 | * | 
|  | 23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
|  | 24 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
|  | 25 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 
|  | 26 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | 
|  | 27 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | 
|  | 28 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 
|  | 29 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
|  | 30 | * SOFTWARE. | 
|  | 31 | * | 
|  | 32 | */ | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 33 | #include <linux/slab.h> | 
| Andy Grover | 922cb17 | 2009-02-24 15:30:20 +0000 | [diff] [blame] | 34 | #include <linux/types.h> | 
|  | 35 | #include <linux/rbtree.h> | 
| Akinobu Mita | 12ce224 | 2011-03-23 16:41:45 -0700 | [diff] [blame] | 36 | #include <linux/bitops.h> | 
| Paul Gortmaker | bc3b2d7 | 2011-07-15 11:47:34 -0400 | [diff] [blame] | 37 | #include <linux/export.h> | 
| David S. Miller | c3b3240 | 2009-03-02 01:49:28 -0800 | [diff] [blame] | 38 |  | 
| Andy Grover | 922cb17 | 2009-02-24 15:30:20 +0000 | [diff] [blame] | 39 | #include "rds.h" | 
|  | 40 |  | 
|  | 41 | /* | 
|  | 42 | * This file implements the receive side of the unconventional congestion | 
|  | 43 | * management in RDS. | 
|  | 44 | * | 
|  | 45 | * Messages waiting in the receive queue on the receiving socket are accounted | 
|  | 46 | * against the sockets SO_RCVBUF option value.  Only the payload bytes in the | 
|  | 47 | * message are accounted for.  If the number of bytes queued equals or exceeds | 
|  | 48 | * rcvbuf then the socket is congested.  All sends attempted to this socket's | 
|  | 49 | * address should return block or return -EWOULDBLOCK. | 
|  | 50 | * | 
|  | 51 | * Applications are expected to be reasonably tuned such that this situation | 
|  | 52 | * very rarely occurs.  An application encountering this "back-pressure" is | 
|  | 53 | * considered a bug. | 
|  | 54 | * | 
|  | 55 | * This is implemented by having each node maintain bitmaps which indicate | 
|  | 56 | * which ports on bound addresses are congested.  As the bitmap changes it is | 
|  | 57 | * sent through all the connections which terminate in the local address of the | 
|  | 58 | * bitmap which changed. | 
|  | 59 | * | 
|  | 60 | * The bitmaps are allocated as connections are brought up.  This avoids | 
|  | 61 | * allocation in the interrupt handling path which queues messages on sockets. | 
|  | 62 | * The dense bitmaps let transports send the entire bitmap on any bitmap change | 
|  | 63 | * reasonably efficiently.  This is much easier to implement than some | 
|  | 64 | * finer-grained communication of per-port congestion.  The sender does a very | 
|  | 65 | * inexpensive bit test to test if the port it's about to send to is congested | 
|  | 66 | * or not. | 
|  | 67 | */ | 
|  | 68 |  | 
|  | 69 | /* | 
|  | 70 | * Interaction with poll is a tad tricky. We want all processes stuck in | 
|  | 71 | * poll to wake up and check whether a congested destination became uncongested. | 
|  | 72 | * The really sad thing is we have no idea which destinations the application | 
|  | 73 | * wants to send to - we don't even know which rds_connections are involved. | 
|  | 74 | * So until we implement a more flexible rds poll interface, we have to make | 
|  | 75 | * do with this: | 
|  | 76 | * We maintain a global counter that is incremented each time a congestion map | 
|  | 77 | * update is received. Each rds socket tracks this value, and if rds_poll | 
|  | 78 | * finds that the saved generation number is smaller than the global generation | 
|  | 79 | * number, it wakes up the process. | 
|  | 80 | */ | 
|  | 81 | static atomic_t		rds_cong_generation = ATOMIC_INIT(0); | 
|  | 82 |  | 
|  | 83 | /* | 
|  | 84 | * Congestion monitoring | 
|  | 85 | */ | 
|  | 86 | static LIST_HEAD(rds_cong_monitor); | 
|  | 87 | static DEFINE_RWLOCK(rds_cong_monitor_lock); | 
|  | 88 |  | 
|  | 89 | /* | 
|  | 90 | * Yes, a global lock.  It's used so infrequently that it's worth keeping it | 
|  | 91 | * global to simplify the locking.  It's only used in the following | 
|  | 92 | * circumstances: | 
|  | 93 | * | 
|  | 94 | *  - on connection buildup to associate a conn with its maps | 
|  | 95 | *  - on map changes to inform conns of a new map to send | 
|  | 96 | * | 
|  | 97 | *  It's sadly ordered under the socket callback lock and the connection lock. | 
|  | 98 | *  Receive paths can mark ports congested from interrupt context so the | 
|  | 99 | *  lock masks interrupts. | 
|  | 100 | */ | 
|  | 101 | static DEFINE_SPINLOCK(rds_cong_lock); | 
|  | 102 | static struct rb_root rds_cong_tree = RB_ROOT; | 
|  | 103 |  | 
|  | 104 | static struct rds_cong_map *rds_cong_tree_walk(__be32 addr, | 
|  | 105 | struct rds_cong_map *insert) | 
|  | 106 | { | 
|  | 107 | struct rb_node **p = &rds_cong_tree.rb_node; | 
|  | 108 | struct rb_node *parent = NULL; | 
|  | 109 | struct rds_cong_map *map; | 
|  | 110 |  | 
|  | 111 | while (*p) { | 
|  | 112 | parent = *p; | 
|  | 113 | map = rb_entry(parent, struct rds_cong_map, m_rb_node); | 
|  | 114 |  | 
|  | 115 | if (addr < map->m_addr) | 
|  | 116 | p = &(*p)->rb_left; | 
|  | 117 | else if (addr > map->m_addr) | 
|  | 118 | p = &(*p)->rb_right; | 
|  | 119 | else | 
|  | 120 | return map; | 
|  | 121 | } | 
|  | 122 |  | 
|  | 123 | if (insert) { | 
|  | 124 | rb_link_node(&insert->m_rb_node, parent, p); | 
|  | 125 | rb_insert_color(&insert->m_rb_node, &rds_cong_tree); | 
|  | 126 | } | 
|  | 127 | return NULL; | 
|  | 128 | } | 
|  | 129 |  | 
|  | 130 | /* | 
|  | 131 | * There is only ever one bitmap for any address.  Connections try and allocate | 
|  | 132 | * these bitmaps in the process getting pointers to them.  The bitmaps are only | 
|  | 133 | * ever freed as the module is removed after all connections have been freed. | 
|  | 134 | */ | 
|  | 135 | static struct rds_cong_map *rds_cong_from_addr(__be32 addr) | 
|  | 136 | { | 
|  | 137 | struct rds_cong_map *map; | 
|  | 138 | struct rds_cong_map *ret = NULL; | 
|  | 139 | unsigned long zp; | 
|  | 140 | unsigned long i; | 
|  | 141 | unsigned long flags; | 
|  | 142 |  | 
|  | 143 | map = kzalloc(sizeof(struct rds_cong_map), GFP_KERNEL); | 
| Andy Grover | 8690bfa | 2010-01-12 11:56:44 -0800 | [diff] [blame] | 144 | if (!map) | 
| Andy Grover | 922cb17 | 2009-02-24 15:30:20 +0000 | [diff] [blame] | 145 | return NULL; | 
|  | 146 |  | 
|  | 147 | map->m_addr = addr; | 
|  | 148 | init_waitqueue_head(&map->m_waitq); | 
|  | 149 | INIT_LIST_HEAD(&map->m_conn_list); | 
|  | 150 |  | 
|  | 151 | for (i = 0; i < RDS_CONG_MAP_PAGES; i++) { | 
|  | 152 | zp = get_zeroed_page(GFP_KERNEL); | 
|  | 153 | if (zp == 0) | 
|  | 154 | goto out; | 
|  | 155 | map->m_page_addrs[i] = zp; | 
|  | 156 | } | 
|  | 157 |  | 
|  | 158 | spin_lock_irqsave(&rds_cong_lock, flags); | 
|  | 159 | ret = rds_cong_tree_walk(addr, map); | 
|  | 160 | spin_unlock_irqrestore(&rds_cong_lock, flags); | 
|  | 161 |  | 
| Andy Grover | 8690bfa | 2010-01-12 11:56:44 -0800 | [diff] [blame] | 162 | if (!ret) { | 
| Andy Grover | 922cb17 | 2009-02-24 15:30:20 +0000 | [diff] [blame] | 163 | ret = map; | 
|  | 164 | map = NULL; | 
|  | 165 | } | 
|  | 166 |  | 
|  | 167 | out: | 
|  | 168 | if (map) { | 
|  | 169 | for (i = 0; i < RDS_CONG_MAP_PAGES && map->m_page_addrs[i]; i++) | 
|  | 170 | free_page(map->m_page_addrs[i]); | 
|  | 171 | kfree(map); | 
|  | 172 | } | 
|  | 173 |  | 
|  | 174 | rdsdebug("map %p for addr %x\n", ret, be32_to_cpu(addr)); | 
|  | 175 |  | 
|  | 176 | return ret; | 
|  | 177 | } | 
|  | 178 |  | 
|  | 179 | /* | 
|  | 180 | * Put the conn on its local map's list.  This is called when the conn is | 
|  | 181 | * really added to the hash.  It's nested under the rds_conn_lock, sadly. | 
|  | 182 | */ | 
|  | 183 | void rds_cong_add_conn(struct rds_connection *conn) | 
|  | 184 | { | 
|  | 185 | unsigned long flags; | 
|  | 186 |  | 
|  | 187 | rdsdebug("conn %p now on map %p\n", conn, conn->c_lcong); | 
|  | 188 | spin_lock_irqsave(&rds_cong_lock, flags); | 
|  | 189 | list_add_tail(&conn->c_map_item, &conn->c_lcong->m_conn_list); | 
|  | 190 | spin_unlock_irqrestore(&rds_cong_lock, flags); | 
|  | 191 | } | 
|  | 192 |  | 
|  | 193 | void rds_cong_remove_conn(struct rds_connection *conn) | 
|  | 194 | { | 
|  | 195 | unsigned long flags; | 
|  | 196 |  | 
|  | 197 | rdsdebug("removing conn %p from map %p\n", conn, conn->c_lcong); | 
|  | 198 | spin_lock_irqsave(&rds_cong_lock, flags); | 
|  | 199 | list_del_init(&conn->c_map_item); | 
|  | 200 | spin_unlock_irqrestore(&rds_cong_lock, flags); | 
|  | 201 | } | 
|  | 202 |  | 
|  | 203 | int rds_cong_get_maps(struct rds_connection *conn) | 
|  | 204 | { | 
|  | 205 | conn->c_lcong = rds_cong_from_addr(conn->c_laddr); | 
|  | 206 | conn->c_fcong = rds_cong_from_addr(conn->c_faddr); | 
|  | 207 |  | 
| Andy Grover | 8690bfa | 2010-01-12 11:56:44 -0800 | [diff] [blame] | 208 | if (!(conn->c_lcong && conn->c_fcong)) | 
| Andy Grover | 922cb17 | 2009-02-24 15:30:20 +0000 | [diff] [blame] | 209 | return -ENOMEM; | 
|  | 210 |  | 
|  | 211 | return 0; | 
|  | 212 | } | 
|  | 213 |  | 
|  | 214 | void rds_cong_queue_updates(struct rds_cong_map *map) | 
|  | 215 | { | 
|  | 216 | struct rds_connection *conn; | 
|  | 217 | unsigned long flags; | 
|  | 218 |  | 
|  | 219 | spin_lock_irqsave(&rds_cong_lock, flags); | 
|  | 220 |  | 
|  | 221 | list_for_each_entry(conn, &map->m_conn_list, c_map_item) { | 
|  | 222 | if (!test_and_set_bit(0, &conn->c_map_queued)) { | 
|  | 223 | rds_stats_inc(s_cong_update_queued); | 
| Andy Grover | 2fa5712 | 2010-03-29 16:45:40 -0700 | [diff] [blame] | 224 | rds_send_xmit(conn); | 
| Andy Grover | 922cb17 | 2009-02-24 15:30:20 +0000 | [diff] [blame] | 225 | } | 
|  | 226 | } | 
|  | 227 |  | 
|  | 228 | spin_unlock_irqrestore(&rds_cong_lock, flags); | 
|  | 229 | } | 
|  | 230 |  | 
|  | 231 | void rds_cong_map_updated(struct rds_cong_map *map, uint64_t portmask) | 
|  | 232 | { | 
|  | 233 | rdsdebug("waking map %p for %pI4\n", | 
|  | 234 | map, &map->m_addr); | 
|  | 235 | rds_stats_inc(s_cong_update_received); | 
|  | 236 | atomic_inc(&rds_cong_generation); | 
|  | 237 | if (waitqueue_active(&map->m_waitq)) | 
|  | 238 | wake_up(&map->m_waitq); | 
|  | 239 | if (waitqueue_active(&rds_poll_waitq)) | 
|  | 240 | wake_up_all(&rds_poll_waitq); | 
|  | 241 |  | 
|  | 242 | if (portmask && !list_empty(&rds_cong_monitor)) { | 
|  | 243 | unsigned long flags; | 
|  | 244 | struct rds_sock *rs; | 
|  | 245 |  | 
|  | 246 | read_lock_irqsave(&rds_cong_monitor_lock, flags); | 
|  | 247 | list_for_each_entry(rs, &rds_cong_monitor, rs_cong_list) { | 
|  | 248 | spin_lock(&rs->rs_lock); | 
|  | 249 | rs->rs_cong_notify |= (rs->rs_cong_mask & portmask); | 
|  | 250 | rs->rs_cong_mask &= ~portmask; | 
|  | 251 | spin_unlock(&rs->rs_lock); | 
|  | 252 | if (rs->rs_cong_notify) | 
|  | 253 | rds_wake_sk_sleep(rs); | 
|  | 254 | } | 
|  | 255 | read_unlock_irqrestore(&rds_cong_monitor_lock, flags); | 
|  | 256 | } | 
|  | 257 | } | 
| Andy Grover | 616b757 | 2009-08-21 12:28:32 +0000 | [diff] [blame] | 258 | EXPORT_SYMBOL_GPL(rds_cong_map_updated); | 
| Andy Grover | 922cb17 | 2009-02-24 15:30:20 +0000 | [diff] [blame] | 259 |  | 
|  | 260 | int rds_cong_updated_since(unsigned long *recent) | 
|  | 261 | { | 
|  | 262 | unsigned long gen = atomic_read(&rds_cong_generation); | 
|  | 263 |  | 
|  | 264 | if (likely(*recent == gen)) | 
|  | 265 | return 0; | 
|  | 266 | *recent = gen; | 
|  | 267 | return 1; | 
|  | 268 | } | 
|  | 269 |  | 
|  | 270 | /* | 
|  | 271 | * We're called under the locking that protects the sockets receive buffer | 
|  | 272 | * consumption.  This makes it a lot easier for the caller to only call us | 
|  | 273 | * when it knows that an existing set bit needs to be cleared, and vice versa. | 
|  | 274 | * We can't block and we need to deal with concurrent sockets working against | 
|  | 275 | * the same per-address map. | 
|  | 276 | */ | 
|  | 277 | void rds_cong_set_bit(struct rds_cong_map *map, __be16 port) | 
|  | 278 | { | 
|  | 279 | unsigned long i; | 
|  | 280 | unsigned long off; | 
|  | 281 |  | 
|  | 282 | rdsdebug("setting congestion for %pI4:%u in map %p\n", | 
|  | 283 | &map->m_addr, ntohs(port), map); | 
|  | 284 |  | 
|  | 285 | i = be16_to_cpu(port) / RDS_CONG_MAP_PAGE_BITS; | 
|  | 286 | off = be16_to_cpu(port) % RDS_CONG_MAP_PAGE_BITS; | 
|  | 287 |  | 
| Akinobu Mita | e1dc1c8 | 2011-03-23 16:42:05 -0700 | [diff] [blame] | 288 | __set_bit_le(off, (void *)map->m_page_addrs[i]); | 
| Andy Grover | 922cb17 | 2009-02-24 15:30:20 +0000 | [diff] [blame] | 289 | } | 
|  | 290 |  | 
|  | 291 | void rds_cong_clear_bit(struct rds_cong_map *map, __be16 port) | 
|  | 292 | { | 
|  | 293 | unsigned long i; | 
|  | 294 | unsigned long off; | 
|  | 295 |  | 
|  | 296 | rdsdebug("clearing congestion for %pI4:%u in map %p\n", | 
|  | 297 | &map->m_addr, ntohs(port), map); | 
|  | 298 |  | 
|  | 299 | i = be16_to_cpu(port) / RDS_CONG_MAP_PAGE_BITS; | 
|  | 300 | off = be16_to_cpu(port) % RDS_CONG_MAP_PAGE_BITS; | 
|  | 301 |  | 
| Akinobu Mita | e1dc1c8 | 2011-03-23 16:42:05 -0700 | [diff] [blame] | 302 | __clear_bit_le(off, (void *)map->m_page_addrs[i]); | 
| Andy Grover | 922cb17 | 2009-02-24 15:30:20 +0000 | [diff] [blame] | 303 | } | 
|  | 304 |  | 
|  | 305 | static int rds_cong_test_bit(struct rds_cong_map *map, __be16 port) | 
|  | 306 | { | 
|  | 307 | unsigned long i; | 
|  | 308 | unsigned long off; | 
|  | 309 |  | 
|  | 310 | i = be16_to_cpu(port) / RDS_CONG_MAP_PAGE_BITS; | 
|  | 311 | off = be16_to_cpu(port) % RDS_CONG_MAP_PAGE_BITS; | 
|  | 312 |  | 
| Akinobu Mita | e1dc1c8 | 2011-03-23 16:42:05 -0700 | [diff] [blame] | 313 | return test_bit_le(off, (void *)map->m_page_addrs[i]); | 
| Andy Grover | 922cb17 | 2009-02-24 15:30:20 +0000 | [diff] [blame] | 314 | } | 
|  | 315 |  | 
|  | 316 | void rds_cong_add_socket(struct rds_sock *rs) | 
|  | 317 | { | 
|  | 318 | unsigned long flags; | 
|  | 319 |  | 
|  | 320 | write_lock_irqsave(&rds_cong_monitor_lock, flags); | 
|  | 321 | if (list_empty(&rs->rs_cong_list)) | 
|  | 322 | list_add(&rs->rs_cong_list, &rds_cong_monitor); | 
|  | 323 | write_unlock_irqrestore(&rds_cong_monitor_lock, flags); | 
|  | 324 | } | 
|  | 325 |  | 
|  | 326 | void rds_cong_remove_socket(struct rds_sock *rs) | 
|  | 327 | { | 
|  | 328 | unsigned long flags; | 
|  | 329 | struct rds_cong_map *map; | 
|  | 330 |  | 
|  | 331 | write_lock_irqsave(&rds_cong_monitor_lock, flags); | 
|  | 332 | list_del_init(&rs->rs_cong_list); | 
|  | 333 | write_unlock_irqrestore(&rds_cong_monitor_lock, flags); | 
|  | 334 |  | 
|  | 335 | /* update congestion map for now-closed port */ | 
|  | 336 | spin_lock_irqsave(&rds_cong_lock, flags); | 
|  | 337 | map = rds_cong_tree_walk(rs->rs_bound_addr, NULL); | 
|  | 338 | spin_unlock_irqrestore(&rds_cong_lock, flags); | 
|  | 339 |  | 
|  | 340 | if (map && rds_cong_test_bit(map, rs->rs_bound_port)) { | 
|  | 341 | rds_cong_clear_bit(map, rs->rs_bound_port); | 
|  | 342 | rds_cong_queue_updates(map); | 
|  | 343 | } | 
|  | 344 | } | 
|  | 345 |  | 
|  | 346 | int rds_cong_wait(struct rds_cong_map *map, __be16 port, int nonblock, | 
|  | 347 | struct rds_sock *rs) | 
|  | 348 | { | 
|  | 349 | if (!rds_cong_test_bit(map, port)) | 
|  | 350 | return 0; | 
|  | 351 | if (nonblock) { | 
|  | 352 | if (rs && rs->rs_cong_monitor) { | 
|  | 353 | unsigned long flags; | 
|  | 354 |  | 
|  | 355 | /* It would have been nice to have an atomic set_bit on | 
|  | 356 | * a uint64_t. */ | 
|  | 357 | spin_lock_irqsave(&rs->rs_lock, flags); | 
|  | 358 | rs->rs_cong_mask |= RDS_CONG_MONITOR_MASK(ntohs(port)); | 
|  | 359 | spin_unlock_irqrestore(&rs->rs_lock, flags); | 
|  | 360 |  | 
|  | 361 | /* Test again - a congestion update may have arrived in | 
|  | 362 | * the meantime. */ | 
|  | 363 | if (!rds_cong_test_bit(map, port)) | 
|  | 364 | return 0; | 
|  | 365 | } | 
|  | 366 | rds_stats_inc(s_cong_send_error); | 
|  | 367 | return -ENOBUFS; | 
|  | 368 | } | 
|  | 369 |  | 
|  | 370 | rds_stats_inc(s_cong_send_blocked); | 
|  | 371 | rdsdebug("waiting on map %p for port %u\n", map, be16_to_cpu(port)); | 
|  | 372 |  | 
|  | 373 | return wait_event_interruptible(map->m_waitq, | 
|  | 374 | !rds_cong_test_bit(map, port)); | 
|  | 375 | } | 
|  | 376 |  | 
|  | 377 | void rds_cong_exit(void) | 
|  | 378 | { | 
|  | 379 | struct rb_node *node; | 
|  | 380 | struct rds_cong_map *map; | 
|  | 381 | unsigned long i; | 
|  | 382 |  | 
|  | 383 | while ((node = rb_first(&rds_cong_tree))) { | 
|  | 384 | map = rb_entry(node, struct rds_cong_map, m_rb_node); | 
|  | 385 | rdsdebug("freeing map %p\n", map); | 
|  | 386 | rb_erase(&map->m_rb_node, &rds_cong_tree); | 
|  | 387 | for (i = 0; i < RDS_CONG_MAP_PAGES && map->m_page_addrs[i]; i++) | 
|  | 388 | free_page(map->m_page_addrs[i]); | 
|  | 389 | kfree(map); | 
|  | 390 | } | 
|  | 391 | } | 
|  | 392 |  | 
|  | 393 | /* | 
|  | 394 | * Allocate a RDS message containing a congestion update. | 
|  | 395 | */ | 
|  | 396 | struct rds_message *rds_cong_update_alloc(struct rds_connection *conn) | 
|  | 397 | { | 
|  | 398 | struct rds_cong_map *map = conn->c_lcong; | 
|  | 399 | struct rds_message *rm; | 
|  | 400 |  | 
|  | 401 | rm = rds_message_map_pages(map->m_page_addrs, RDS_CONG_MAP_BYTES); | 
|  | 402 | if (!IS_ERR(rm)) | 
|  | 403 | rm->m_inc.i_hdr.h_flags = RDS_FLAG_CONG_BITMAP; | 
|  | 404 |  | 
|  | 405 | return rm; | 
|  | 406 | } |