/* returns -ve errno or +ve port */ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) { int ret = -EADDRINUSE; u16 rover, last; u64 key; if (*port != 0) { rover = be16_to_cpu(*port); if (rover == RDS_FLAG_PROBE_PORT) return -EINVAL; last = rover; } else { rover = max_t(u16, prandom_u32(), 2); last = rover - 1; } do { if (rover == 0) rover++; if (rover == RDS_FLAG_PROBE_PORT) continue; key = ((u64)addr << 32) | cpu_to_be16(rover); if (rhashtable_lookup_fast(&bind_hash_table, &key, ht_parms)) continue; rs->rs_bound_key = key; rs->rs_bound_addr = addr; net_get_random_once(&rs->rs_hash_initval, sizeof(rs->rs_hash_initval)); rs->rs_bound_port = cpu_to_be16(rover); rs->rs_bound_node.next = NULL; rds_sock_addref(rs); if (!rhashtable_insert_fast(&bind_hash_table, &rs->rs_bound_node, ht_parms)) { *port = rs->rs_bound_port; ret = 0; rdsdebug("rs %p binding to %pI4:%d\n", rs, &addr, (int)ntohs(*port)); break; } else { rds_sock_put(rs); ret = -ENOMEM; break; } } while (rover++ != last); return ret; }
/* * Return the rds_sock bound at the given local address. * * The rx path can race with rds_release. We notice if rds_release() has * marked this socket and don't return a rs ref to the rx path. */ struct rds_sock *rds_find_bound(__be32 addr, __be16 port) { u64 key = ((u64)addr << 32) | port; struct rds_sock *rs; rs = rhashtable_lookup_fast(&bind_hash_table, &key, ht_parms); if (rs && !sock_flag(rds_rs_to_sk(rs), SOCK_DEAD)) rds_sock_addref(rs); else rs = NULL; rdsdebug("returning rs %p for %pI4:%u\n", rs, &addr, ntohs(port)); return rs; }
/* * Return the rds_sock bound at the given local address. * * The rx path can race with rds_release. We notice if rds_release() has * marked this socket and don't return a rs ref to the rx path. */ struct rds_sock *rds_find_bound(__be32 addr, __be16 port) { struct rds_sock *rs; unsigned long flags; spin_lock_irqsave(&rds_bind_lock, flags); rs = rds_bind_tree_walk(addr, port, NULL); if (rs && !sock_flag(rds_rs_to_sk(rs), SOCK_DEAD)) rds_sock_addref(rs); else rs = NULL; spin_unlock_irqrestore(&rds_bind_lock, flags); rdsdebug("returning rs %p for %pI4:%u\n", rs, &addr, ntohs(port)); return rs; }
/* must hold either read or write lock (write lock for insert != NULL) */ static struct rds_sock *rds_bind_lookup(struct bind_bucket *bucket, __be32 addr, __be16 port, struct rds_sock *insert) { struct rds_sock *rs; struct hlist_head *head = &bucket->head; u64 cmp; u64 needle = ((u64)be32_to_cpu(addr) << 32) | be16_to_cpu(port); hlist_for_each_entry(rs, head, rs_bound_node) { cmp = ((u64)be32_to_cpu(rs->rs_bound_addr) << 32) | be16_to_cpu(rs->rs_bound_port); if (cmp == needle) { rds_sock_addref(rs); return rs; } }
/* returns -ve errno or +ve port */ static int rds_add_bound(struct rds_sock *rs, __be32 addr, __be16 *port) { unsigned long flags; int ret = -EADDRINUSE; u16 rover, last; if (*port != 0) { rover = be16_to_cpu(*port); last = rover; } else { rover = max_t(u16, net_random(), 2); last = rover - 1; } spin_lock_irqsave(&rds_bind_lock, flags); do { if (rover == 0) rover++; if (rds_bind_tree_walk(addr, cpu_to_be16(rover), rs) == NULL) { *port = cpu_to_be16(rover); ret = 0; break; } } while (rover++ != last); if (ret == 0) { rs->rs_bound_addr = addr; rs->rs_bound_port = *port; rds_sock_addref(rs); rdsdebug("rs %p binding to %pI4:%d\n", rs, &addr, (int)ntohs(*port)); } spin_unlock_irqrestore(&rds_bind_lock, flags); return ret; }