[NET]: UDPv4 and UDPv6 use an almost identical version of the get_port function,
which is unnecessary since the (long) code differs in only one if-statement.
This patch creates one common function which is called by udp_v4_get_port() and
udp_v6_get_port(). As a result,
* duplicated code is removed
* udp_port_rover and local port lookup can now be removed from udp.h
* further savings follow since the same function will be used by UDP-Litev4
and UDP-Litev6
In contrast to the patch sent in response to Yoshifujis comments (fixed by this
variant), the code below also removes the EXPORT_SYMBOL(udp_port_rover), since
udp_port_rover can now remain local to net/ipv4/udp.c.
Signed-off-by: Gerrit Renker <[email protected]>
---
include/net/udp.h | 18 +---------
net/ipv4/udp.c | 96 +++++++++++++++++++++++++++++++++---------------------
net/ipv6/udp.c | 76 +-----------------------------------------
3 files changed, 64 insertions(+), 126 deletions(-)
diff --git a/include/net/udp.h b/include/net/udp.h
index 766fba1..c490a0f 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -30,25 +30,9 @@ #include <linux/seq_file.h>
#define UDP_HTABLE_SIZE 128
-/* udp.c: This needs to be shared by v4 and v6 because the lookup
- * and hashing code needs to work with different AF's yet
- * the port space is shared.
- */
extern struct hlist_head udp_hash[UDP_HTABLE_SIZE];
extern rwlock_t udp_hash_lock;
-extern int udp_port_rover;
-
-static inline int udp_lport_inuse(u16 num)
-{
- struct sock *sk;
- struct hlist_node *node;
-
- sk_for_each(sk, node, &udp_hash[num & (UDP_HTABLE_SIZE - 1)])
- if (inet_sk(sk)->num == num)
- return 1;
- return 0;
-}
/* Note: this must match 'valbool' in sock_setsockopt */
#define UDP_CSUM_NOXMIT 1
@@ -63,6 +47,8 @@ extern struct proto udp_prot;
struct sk_buff;
+extern int udp_get_port(struct sock *sk, unsigned short snum,
+ int (*saddr_cmp)(struct sock *, struct sock *));
extern void udp_err(struct sk_buff *, u32);
extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk,
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 3f93292..9d4e156 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -119,14 +119,34 @@ DEFINE_SNMP_STAT(struct udp_mib, udp_sta
struct hlist_head udp_hash[UDP_HTABLE_SIZE];
DEFINE_RWLOCK(udp_hash_lock);
-/* Shared by v4/v6 udp. */
+/* Shared by v4/v6 udp_get_port */
int udp_port_rover;
-static int udp_v4_get_port(struct sock *sk, unsigned short snum)
+static inline int udp_lport_inuse(u16 num)
{
+ struct sock *sk;
struct hlist_node *node;
+
+ sk_for_each(sk, node, &udp_hash[num & (UDP_HTABLE_SIZE - 1)])
+ if (inet_sk(sk)->num == num)
+ return 1;
+ return 0;
+}
+
+/**
+ * udp_get_port - common port lookup for IPv4 and IPv6
+ *
+ * @sk: socket struct in question
+ * @snum: port number to look up
+ * @saddr_comp: AF-dependent comparison of bound local IP addresses
+ */
+int udp_get_port(struct sock *sk, unsigned short snum,
+ int (*saddr_cmp)(struct sock *sk1, struct sock *sk2))
+{
+ struct hlist_node *node;
+ struct hlist_head *head;
struct sock *sk2;
- struct inet_sock *inet = inet_sk(sk);
+ int error = 1;
write_lock_bh(&udp_hash_lock);
if (snum == 0) {
@@ -138,11 +158,10 @@ static int udp_v4_get_port(struct sock *
best_size_so_far = 32767;
best = result = udp_port_rover;
for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
- struct hlist_head *list;
int size;
- list = &udp_hash[result & (UDP_HTABLE_SIZE - 1)];
- if (hlist_empty(list)) {
+ head = &udp_hash[result & (UDP_HTABLE_SIZE - 1)];
+ if (hlist_empty(head)) {
if (result > sysctl_local_port_range[1])
result = sysctl_local_port_range[0] +
((result - sysctl_local_port_range[0]) &
@@ -150,12 +169,11 @@ static int udp_v4_get_port(struct sock *
goto gotit;
}
size = 0;
- sk_for_each(sk2, node, list)
- if (++size >= best_size_so_far)
- goto next;
- best_size_so_far = size;
- best = result;
- next:;
+ sk_for_each(sk2, node, head)
+ if (++size < best_size_so_far) {
+ best_size_so_far = size;
+ best = result;
+ }
}
result = best;
for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) {
@@ -171,38 +189,44 @@ static int udp_v4_get_port(struct sock *
gotit:
udp_port_rover = snum = result;
} else {
- sk_for_each(sk2, node,
- &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]) {
- struct inet_sock *inet2 = inet_sk(sk2);
-
- if (inet2->num == snum &&
- sk2 != sk &&
- !ipv6_only_sock(sk2) &&
- (!sk2->sk_bound_dev_if ||
- !sk->sk_bound_dev_if ||
- sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
- (!inet2->rcv_saddr ||
- !inet->rcv_saddr ||
- inet2->rcv_saddr == inet->rcv_saddr) &&
- (!sk2->sk_reuse || !sk->sk_reuse))
+ head = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
+
+ sk_for_each(sk2, node, head)
+ if (inet_sk(sk2)->num == snum &&
+ sk2 != sk &&
+ (!sk2->sk_reuse || !sk->sk_reuse) &&
+ (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if
+ || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
+ (*saddr_cmp)(sk, sk2) )
goto fail;
- }
}
- inet->num = snum;
+ inet_sk(sk)->num = snum;
if (sk_unhashed(sk)) {
- struct hlist_head *h = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
-
- sk_add_node(sk, h);
+ head = &udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
+ sk_add_node(sk, head);
sock_prot_inc_use(sk->sk_prot);
}
- write_unlock_bh(&udp_hash_lock);
- return 0;
-
+ error = 0;
fail:
write_unlock_bh(&udp_hash_lock);
- return 1;
+ return error;
+}
+
+static inline int ipv4_rcv_saddr_equal(struct sock *sk1, struct sock *sk2)
+{
+ struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2);
+
+ return ( !ipv6_only_sock(sk2) &&
+ (!inet1->rcv_saddr || !inet2->rcv_saddr ||
+ inet1->rcv_saddr == inet2->rcv_saddr ));
+}
+
+static inline int udp_v4_get_port(struct sock *sk, unsigned short snum)
+{
+ return udp_get_port(sk, snum, ipv4_rcv_saddr_equal);
}
+
static void udp_v4_hash(struct sock *sk)
{
BUG();
@@ -1583,7 +1607,7 @@ EXPORT_SYMBOL(udp_disconnect);
EXPORT_SYMBOL(udp_hash);
EXPORT_SYMBOL(udp_hash_lock);
EXPORT_SYMBOL(udp_ioctl);
-EXPORT_SYMBOL(udp_port_rover);
+EXPORT_SYMBOL(udp_get_port);
EXPORT_SYMBOL(udp_prot);
EXPORT_SYMBOL(udp_sendmsg);
EXPORT_SYMBOL(udp_poll);
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 8d3432a..74fd59a 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -62,81 +62,9 @@ #include <linux/seq_file.h>
DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6) __read_mostly;
-/* Grrr, addr_type already calculated by caller, but I don't want
- * to add some silly "cookie" argument to this method just for that.
- */
-static int udp_v6_get_port(struct sock *sk, unsigned short snum)
+static inline int udp_v6_get_port(struct sock *sk, unsigned short snum)
{
- struct sock *sk2;
- struct hlist_node *node;
-
- write_lock_bh(&udp_hash_lock);
- if (snum == 0) {
- int best_size_so_far, best, result, i;
-
- if (udp_port_rover > sysctl_local_port_range[1] ||
- udp_port_rover < sysctl_local_port_range[0])
- udp_port_rover = sysctl_local_port_range[0];
- best_size_so_far = 32767;
- best = result = udp_port_rover;
- for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
- int size;
- struct hlist_head *list;
-
- list = &udp_hash[result & (UDP_HTABLE_SIZE - 1)];
- if (hlist_empty(list)) {
- if (result > sysctl_local_port_range[1])
- result = sysctl_local_port_range[0] +
- ((result - sysctl_local_port_range[0]) &
- (UDP_HTABLE_SIZE - 1));
- goto gotit;
- }
- size = 0;
- sk_for_each(sk2, node, list)
- if (++size >= best_size_so_far)
- goto next;
- best_size_so_far = size;
- best = result;
- next:;
- }
- result = best;
- for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, result += UDP_HTABLE_SIZE) {
- if (result > sysctl_local_port_range[1])
- result = sysctl_local_port_range[0]
- + ((result - sysctl_local_port_range[0]) &
- (UDP_HTABLE_SIZE - 1));
- if (!udp_lport_inuse(result))
- break;
- }
- if (i >= (1 << 16) / UDP_HTABLE_SIZE)
- goto fail;
-gotit:
- udp_port_rover = snum = result;
- } else {
- sk_for_each(sk2, node,
- &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]) {
- if (inet_sk(sk2)->num == snum &&
- sk2 != sk &&
- (!sk2->sk_bound_dev_if ||
- !sk->sk_bound_dev_if ||
- sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
- (!sk2->sk_reuse || !sk->sk_reuse) &&
- ipv6_rcv_saddr_equal(sk, sk2))
- goto fail;
- }
- }
-
- inet_sk(sk)->num = snum;
- if (sk_unhashed(sk)) {
- sk_add_node(sk, &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]);
- sock_prot_inc_use(sk->sk_prot);
- }
- write_unlock_bh(&udp_hash_lock);
- return 0;
-
-fail:
- write_unlock_bh(&udp_hash_lock);
- return 1;
+ return udp_get_port(sk, snum, ipv6_rcv_saddr_equal);
}
static void udp_v6_hash(struct sock *sk)
On Thu, 17 Aug 2006, [email protected] wrote:
> - if (inet2->num == snum &&
> - sk2 != sk &&
> - !ipv6_only_sock(sk2) &&
> - (!sk2->sk_bound_dev_if ||
> - !sk->sk_bound_dev_if ||
> - sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
> + sk_for_each(sk2, node, head)
> + if (inet_sk(sk2)->num == snum &&
> + sk2 != sk &&
> + (!sk2->sk_reuse || !sk->sk_reuse) &&
> + (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if
> + || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
Doesn't this change the behavior for IPV6_V6ONLY sockets ?
- James
--
James Morris
<[email protected]>
Hi,
In article <[email protected]> (at Thu, 17 Aug 2006 10:56:40 -0400 (EDT)), James Morris <[email protected]> says:
> On Thu, 17 Aug 2006, [email protected] wrote:
>
> > - if (inet2->num == snum &&
> > - sk2 != sk &&
> > - !ipv6_only_sock(sk2) &&
> > - (!sk2->sk_bound_dev_if ||
> > - !sk->sk_bound_dev_if ||
> > - sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
>
>
> > + sk_for_each(sk2, node, head)
> > + if (inet_sk(sk2)->num == snum &&
> > + sk2 != sk &&
> > + (!sk2->sk_reuse || !sk->sk_reuse) &&
> > + (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if
> > + || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
>
>
> Doesn't this change the behavior for IPV6_V6ONLY sockets ?
It is tested in ipv4_rcv_saddr_equal() (called vi saddr_cmp), isn't it?
--yoshfuji
From: [email protected]
Date: Thu, 17 Aug 2006 13:25:46 +0100
> [NET]: UDPv4 and UDPv6 use an almost identical version of the get_port function,
> which is unnecessary since the (long) code differs in only one if-statement.
>
> This patch creates one common function which is called by udp_v4_get_port() and
> udp_v6_get_port(). As a result,
> * duplicated code is removed
> * udp_port_rover and local port lookup can now be removed from udp.h
> * further savings follow since the same function will be used by UDP-Litev4
> and UDP-Litev6
>
> In contrast to the patch sent in response to Yoshifujis comments (fixed by this
> variant), the code below also removes the EXPORT_SYMBOL(udp_port_rover), since
> udp_port_rover can now remain local to net/ipv4/udp.c.
>
> Signed-off-by: Gerrit Renker <[email protected]>
Applied, and I marked udp_port_rover "static" for
good measure.
Thanks.