diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/ipv4/tcp_metrics.c | 151 |
1 files changed, 103 insertions, 48 deletions
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 06493736fbc8..699a42faab9c 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c | |||
| @@ -31,7 +31,8 @@ struct tcp_fastopen_metrics { | |||
| 31 | 31 | ||
| 32 | struct tcp_metrics_block { | 32 | struct tcp_metrics_block { |
| 33 | struct tcp_metrics_block __rcu *tcpm_next; | 33 | struct tcp_metrics_block __rcu *tcpm_next; |
| 34 | struct inetpeer_addr tcpm_addr; | 34 | struct inetpeer_addr tcpm_saddr; |
| 35 | struct inetpeer_addr tcpm_daddr; | ||
| 35 | unsigned long tcpm_stamp; | 36 | unsigned long tcpm_stamp; |
| 36 | u32 tcpm_ts; | 37 | u32 tcpm_ts; |
| 37 | u32 tcpm_ts_stamp; | 38 | u32 tcpm_ts_stamp; |
| @@ -131,7 +132,8 @@ static void tcpm_suck_dst(struct tcp_metrics_block *tm, struct dst_entry *dst, | |||
| 131 | } | 132 | } |
| 132 | 133 | ||
| 133 | static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, | 134 | static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, |
| 134 | struct inetpeer_addr *addr, | 135 | struct inetpeer_addr *saddr, |
| 136 | struct inetpeer_addr *daddr, | ||
| 135 | unsigned int hash, | 137 | unsigned int hash, |
| 136 | bool reclaim) | 138 | bool reclaim) |
| 137 | { | 139 | { |
| @@ -155,7 +157,8 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, | |||
| 155 | if (!tm) | 157 | if (!tm) |
| 156 | goto out_unlock; | 158 | goto out_unlock; |
| 157 | } | 159 | } |
| 158 | tm->tcpm_addr = *addr; | 160 | tm->tcpm_saddr = *saddr; |
| 161 | tm->tcpm_daddr = *daddr; | ||
| 159 | 162 | ||
| 160 | tcpm_suck_dst(tm, dst, true); | 163 | tcpm_suck_dst(tm, dst, true); |
| 161 | 164 | ||
| @@ -189,7 +192,8 @@ static struct tcp_metrics_block *tcp_get_encode(struct tcp_metrics_block *tm, in | |||
| 189 | return NULL; | 192 | return NULL; |
| 190 | } | 193 | } |
| 191 | 194 | ||
| 192 | static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *addr, | 195 | static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *saddr, |
| 196 | const struct inetpeer_addr *daddr, | ||
| 193 | struct net *net, unsigned int hash) | 197 | struct net *net, unsigned int hash) |
| 194 | { | 198 | { |
| 195 | struct tcp_metrics_block *tm; | 199 | struct tcp_metrics_block *tm; |
| @@ -197,7 +201,8 @@ static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *a | |||
| 197 | 201 | ||
| 198 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; | 202 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; |
| 199 | tm = rcu_dereference(tm->tcpm_next)) { | 203 | tm = rcu_dereference(tm->tcpm_next)) { |
| 200 | if (addr_same(&tm->tcpm_addr, addr)) | 204 | if (addr_same(&tm->tcpm_saddr, saddr) && |
| 205 | addr_same(&tm->tcpm_daddr, daddr)) | ||
| 201 | break; | 206 | break; |
| 202 | depth++; | 207 | depth++; |
| 203 | } | 208 | } |
| @@ -208,19 +213,22 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, | |||
| 208 | struct dst_entry *dst) | 213 | struct dst_entry *dst) |
| 209 | { | 214 | { |
| 210 | struct tcp_metrics_block *tm; | 215 | struct tcp_metrics_block *tm; |
| 211 | struct inetpeer_addr addr; | 216 | struct inetpeer_addr saddr, daddr; |
| 212 | unsigned int hash; | 217 | unsigned int hash; |
| 213 | struct net *net; | 218 | struct net *net; |
| 214 | 219 | ||
| 215 | addr.family = req->rsk_ops->family; | 220 | saddr.family = req->rsk_ops->family; |
| 216 | switch (addr.family) { | 221 | daddr.family = req->rsk_ops->family; |
| 222 | switch (daddr.family) { | ||
| 217 | case AF_INET: | 223 | case AF_INET: |
| 218 | addr.addr.a4 = inet_rsk(req)->ir_rmt_addr; | 224 | saddr.addr.a4 = inet_rsk(req)->ir_loc_addr; |
| 219 | hash = (__force unsigned int) addr.addr.a4; | 225 | daddr.addr.a4 = inet_rsk(req)->ir_rmt_addr; |
| 226 | hash = (__force unsigned int) daddr.addr.a4; | ||
| 220 | break; | 227 | break; |
| 221 | #if IS_ENABLED(CONFIG_IPV6) | 228 | #if IS_ENABLED(CONFIG_IPV6) |
| 222 | case AF_INET6: | 229 | case AF_INET6: |
| 223 | *(struct in6_addr *)addr.addr.a6 = inet_rsk(req)->ir_v6_rmt_addr; | 230 | *(struct in6_addr *)saddr.addr.a6 = inet_rsk(req)->ir_v6_loc_addr; |
| 231 | *(struct in6_addr *)daddr.addr.a6 = inet_rsk(req)->ir_v6_rmt_addr; | ||
| 224 | hash = ipv6_addr_hash(&inet_rsk(req)->ir_v6_rmt_addr); | 232 | hash = ipv6_addr_hash(&inet_rsk(req)->ir_v6_rmt_addr); |
| 225 | break; | 233 | break; |
| 226 | #endif | 234 | #endif |
| @@ -233,7 +241,8 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, | |||
| 233 | 241 | ||
| 234 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; | 242 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; |
| 235 | tm = rcu_dereference(tm->tcpm_next)) { | 243 | tm = rcu_dereference(tm->tcpm_next)) { |
| 236 | if (addr_same(&tm->tcpm_addr, &addr)) | 244 | if (addr_same(&tm->tcpm_saddr, &saddr) && |
| 245 | addr_same(&tm->tcpm_daddr, &daddr)) | ||
| 237 | break; | 246 | break; |
| 238 | } | 247 | } |
| 239 | tcpm_check_stamp(tm, dst); | 248 | tcpm_check_stamp(tm, dst); |
| @@ -243,19 +252,22 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, | |||
| 243 | static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock *tw) | 252 | static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock *tw) |
| 244 | { | 253 | { |
| 245 | struct tcp_metrics_block *tm; | 254 | struct tcp_metrics_block *tm; |
| 246 | struct inetpeer_addr addr; | 255 | struct inetpeer_addr saddr, daddr; |
| 247 | unsigned int hash; | 256 | unsigned int hash; |
| 248 | struct net *net; | 257 | struct net *net; |
| 249 | 258 | ||
| 250 | addr.family = tw->tw_family; | 259 | saddr.family = tw->tw_family; |
| 251 | switch (addr.family) { | 260 | daddr.family = tw->tw_family; |
| 261 | switch (daddr.family) { | ||
| 252 | case AF_INET: | 262 | case AF_INET: |
| 253 | addr.addr.a4 = tw->tw_daddr; | 263 | saddr.addr.a4 = tw->tw_rcv_saddr; |
| 254 | hash = (__force unsigned int) addr.addr.a4; | 264 | daddr.addr.a4 = tw->tw_daddr; |
| 265 | hash = (__force unsigned int) daddr.addr.a4; | ||
| 255 | break; | 266 | break; |
| 256 | #if IS_ENABLED(CONFIG_IPV6) | 267 | #if IS_ENABLED(CONFIG_IPV6) |
| 257 | case AF_INET6: | 268 | case AF_INET6: |
| 258 | *(struct in6_addr *)addr.addr.a6 = tw->tw_v6_daddr; | 269 | *(struct in6_addr *)saddr.addr.a6 = tw->tw_v6_rcv_saddr; |
| 270 | *(struct in6_addr *)daddr.addr.a6 = tw->tw_v6_daddr; | ||
| 259 | hash = ipv6_addr_hash(&tw->tw_v6_daddr); | 271 | hash = ipv6_addr_hash(&tw->tw_v6_daddr); |
| 260 | break; | 272 | break; |
| 261 | #endif | 273 | #endif |
| @@ -268,7 +280,8 @@ static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock | |||
| 268 | 280 | ||
| 269 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; | 281 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; |
| 270 | tm = rcu_dereference(tm->tcpm_next)) { | 282 | tm = rcu_dereference(tm->tcpm_next)) { |
| 271 | if (addr_same(&tm->tcpm_addr, &addr)) | 283 | if (addr_same(&tm->tcpm_saddr, &saddr) && |
| 284 | addr_same(&tm->tcpm_daddr, &daddr)) | ||
| 272 | break; | 285 | break; |
| 273 | } | 286 | } |
| 274 | return tm; | 287 | return tm; |
| @@ -279,20 +292,23 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, | |||
| 279 | bool create) | 292 | bool create) |
| 280 | { | 293 | { |
| 281 | struct tcp_metrics_block *tm; | 294 | struct tcp_metrics_block *tm; |
| 282 | struct inetpeer_addr addr; | 295 | struct inetpeer_addr saddr, daddr; |
| 283 | unsigned int hash; | 296 | unsigned int hash; |
| 284 | struct net *net; | 297 | struct net *net; |
| 285 | bool reclaim; | 298 | bool reclaim; |
| 286 | 299 | ||
| 287 | addr.family = sk->sk_family; | 300 | saddr.family = sk->sk_family; |
| 288 | switch (addr.family) { | 301 | daddr.family = sk->sk_family; |
| 302 | switch (daddr.family) { | ||
| 289 | case AF_INET: | 303 | case AF_INET: |
| 290 | addr.addr.a4 = inet_sk(sk)->inet_daddr; | 304 | saddr.addr.a4 = inet_sk(sk)->inet_saddr; |
| 291 | hash = (__force unsigned int) addr.addr.a4; | 305 | daddr.addr.a4 = inet_sk(sk)->inet_daddr; |
| 306 | hash = (__force unsigned int) daddr.addr.a4; | ||
| 292 | break; | 307 | break; |
| 293 | #if IS_ENABLED(CONFIG_IPV6) | 308 | #if IS_ENABLED(CONFIG_IPV6) |
| 294 | case AF_INET6: | 309 | case AF_INET6: |
| 295 | *(struct in6_addr *)addr.addr.a6 = sk->sk_v6_daddr; | 310 | *(struct in6_addr *)saddr.addr.a6 = sk->sk_v6_rcv_saddr; |
| 311 | *(struct in6_addr *)daddr.addr.a6 = sk->sk_v6_daddr; | ||
| 296 | hash = ipv6_addr_hash(&sk->sk_v6_daddr); | 312 | hash = ipv6_addr_hash(&sk->sk_v6_daddr); |
| 297 | break; | 313 | break; |
| 298 | #endif | 314 | #endif |
| @@ -303,14 +319,14 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, | |||
| 303 | net = dev_net(dst->dev); | 319 | net = dev_net(dst->dev); |
| 304 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); | 320 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); |
| 305 | 321 | ||
| 306 | tm = __tcp_get_metrics(&addr, net, hash); | 322 | tm = __tcp_get_metrics(&saddr, &daddr, net, hash); |
| 307 | reclaim = false; | 323 | reclaim = false; |
| 308 | if (tm == TCP_METRICS_RECLAIM_PTR) { | 324 | if (tm == TCP_METRICS_RECLAIM_PTR) { |
| 309 | reclaim = true; | 325 | reclaim = true; |
| 310 | tm = NULL; | 326 | tm = NULL; |
| 311 | } | 327 | } |
| 312 | if (!tm && create) | 328 | if (!tm && create) |
| 313 | tm = tcpm_new(dst, &addr, hash, reclaim); | 329 | tm = tcpm_new(dst, &saddr, &daddr, hash, reclaim); |
| 314 | else | 330 | else |
| 315 | tcpm_check_stamp(tm, dst); | 331 | tcpm_check_stamp(tm, dst); |
| 316 | 332 | ||
| @@ -724,15 +740,21 @@ static int tcp_metrics_fill_info(struct sk_buff *msg, | |||
| 724 | struct nlattr *nest; | 740 | struct nlattr *nest; |
| 725 | int i; | 741 | int i; |
| 726 | 742 | ||
| 727 | switch (tm->tcpm_addr.family) { | 743 | switch (tm->tcpm_daddr.family) { |
| 728 | case AF_INET: | 744 | case AF_INET: |
| 729 | if (nla_put_be32(msg, TCP_METRICS_ATTR_ADDR_IPV4, | 745 | if (nla_put_be32(msg, TCP_METRICS_ATTR_ADDR_IPV4, |
| 730 | tm->tcpm_addr.addr.a4) < 0) | 746 | tm->tcpm_daddr.addr.a4) < 0) |
| 747 | goto nla_put_failure; | ||
| 748 | if (nla_put_be32(msg, TCP_METRICS_ATTR_SADDR_IPV4, | ||
| 749 | tm->tcpm_saddr.addr.a4) < 0) | ||
| 731 | goto nla_put_failure; | 750 | goto nla_put_failure; |
| 732 | break; | 751 | break; |
| 733 | case AF_INET6: | 752 | case AF_INET6: |
| 734 | if (nla_put(msg, TCP_METRICS_ATTR_ADDR_IPV6, 16, | 753 | if (nla_put(msg, TCP_METRICS_ATTR_ADDR_IPV6, 16, |
| 735 | tm->tcpm_addr.addr.a6) < 0) | 754 | tm->tcpm_daddr.addr.a6) < 0) |
| 755 | goto nla_put_failure; | ||
| 756 | if (nla_put(msg, TCP_METRICS_ATTR_SADDR_IPV6, 16, | ||
| 757 | tm->tcpm_saddr.addr.a6) < 0) | ||
| 736 | goto nla_put_failure; | 758 | goto nla_put_failure; |
| 737 | break; | 759 | break; |
| 738 | default: | 760 | default: |
| @@ -855,44 +877,66 @@ done: | |||
| 855 | return skb->len; | 877 | return skb->len; |
| 856 | } | 878 | } |
| 857 | 879 | ||
| 858 | static int parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, | 880 | static int __parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, |
| 859 | unsigned int *hash, int optional) | 881 | unsigned int *hash, int optional, int v4, int v6) |
| 860 | { | 882 | { |
| 861 | struct nlattr *a; | 883 | struct nlattr *a; |
| 862 | 884 | ||
| 863 | a = info->attrs[TCP_METRICS_ATTR_ADDR_IPV4]; | 885 | a = info->attrs[v4]; |
| 864 | if (a) { | 886 | if (a) { |
| 865 | addr->family = AF_INET; | 887 | addr->family = AF_INET; |
| 866 | addr->addr.a4 = nla_get_be32(a); | 888 | addr->addr.a4 = nla_get_be32(a); |
| 867 | *hash = (__force unsigned int) addr->addr.a4; | 889 | if (hash) |
| 890 | *hash = (__force unsigned int) addr->addr.a4; | ||
| 868 | return 0; | 891 | return 0; |
| 869 | } | 892 | } |
| 870 | a = info->attrs[TCP_METRICS_ATTR_ADDR_IPV6]; | 893 | a = info->attrs[v6]; |
| 871 | if (a) { | 894 | if (a) { |
| 872 | if (nla_len(a) != sizeof(struct in6_addr)) | 895 | if (nla_len(a) != sizeof(struct in6_addr)) |
| 873 | return -EINVAL; | 896 | return -EINVAL; |
| 874 | addr->family = AF_INET6; | 897 | addr->family = AF_INET6; |
| 875 | memcpy(addr->addr.a6, nla_data(a), sizeof(addr->addr.a6)); | 898 | memcpy(addr->addr.a6, nla_data(a), sizeof(addr->addr.a6)); |
| 876 | *hash = ipv6_addr_hash((struct in6_addr *) addr->addr.a6); | 899 | if (hash) |
| 900 | *hash = ipv6_addr_hash((struct in6_addr *) addr->addr.a6); | ||
| 877 | return 0; | 901 | return 0; |
| 878 | } | 902 | } |
| 879 | return optional ? 1 : -EAFNOSUPPORT; | 903 | return optional ? 1 : -EAFNOSUPPORT; |
| 880 | } | 904 | } |
| 881 | 905 | ||
| 906 | static int parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr, | ||
| 907 | unsigned int *hash, int optional) | ||
| 908 | { | ||
| 909 | return __parse_nl_addr(info, addr, hash, optional, | ||
| 910 | TCP_METRICS_ATTR_ADDR_IPV4, | ||
| 911 | TCP_METRICS_ATTR_ADDR_IPV6); | ||
| 912 | } | ||
| 913 | |||
| 914 | static int parse_nl_saddr(struct genl_info *info, struct inetpeer_addr *addr) | ||
| 915 | { | ||
| 916 | return __parse_nl_addr(info, addr, NULL, 0, | ||
| 917 | TCP_METRICS_ATTR_SADDR_IPV4, | ||
| 918 | TCP_METRICS_ATTR_SADDR_IPV6); | ||
| 919 | } | ||
| 920 | |||
| 882 | static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) | 921 | static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) |
| 883 | { | 922 | { |
| 884 | struct tcp_metrics_block *tm; | 923 | struct tcp_metrics_block *tm; |
| 885 | struct inetpeer_addr addr; | 924 | struct inetpeer_addr saddr, daddr; |
| 886 | unsigned int hash; | 925 | unsigned int hash; |
| 887 | struct sk_buff *msg; | 926 | struct sk_buff *msg; |
| 888 | struct net *net = genl_info_net(info); | 927 | struct net *net = genl_info_net(info); |
| 889 | void *reply; | 928 | void *reply; |
| 890 | int ret; | 929 | int ret; |
| 930 | bool src = true; | ||
| 891 | 931 | ||
| 892 | ret = parse_nl_addr(info, &addr, &hash, 0); | 932 | ret = parse_nl_addr(info, &daddr, &hash, 0); |
| 893 | if (ret < 0) | 933 | if (ret < 0) |
| 894 | return ret; | 934 | return ret; |
| 895 | 935 | ||
| 936 | ret = parse_nl_saddr(info, &saddr); | ||
| 937 | if (ret < 0) | ||
| 938 | src = false; | ||
| 939 | |||
| 896 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 940 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
| 897 | if (!msg) | 941 | if (!msg) |
| 898 | return -ENOMEM; | 942 | return -ENOMEM; |
| @@ -907,7 +951,8 @@ static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info) | |||
| 907 | rcu_read_lock(); | 951 | rcu_read_lock(); |
| 908 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; | 952 | for (tm = rcu_dereference(net->ipv4.tcp_metrics_hash[hash].chain); tm; |
| 909 | tm = rcu_dereference(tm->tcpm_next)) { | 953 | tm = rcu_dereference(tm->tcpm_next)) { |
| 910 | if (addr_same(&tm->tcpm_addr, &addr)) { | 954 | if (addr_same(&tm->tcpm_daddr, &daddr) && |
| 955 | (!src || addr_same(&tm->tcpm_saddr, &saddr))) { | ||
| 911 | ret = tcp_metrics_fill_info(msg, tm); | 956 | ret = tcp_metrics_fill_info(msg, tm); |
| 912 | break; | 957 | break; |
| 913 | } | 958 | } |
| @@ -960,34 +1005,44 @@ static int tcp_metrics_flush_all(struct net *net) | |||
| 960 | static int tcp_metrics_nl_cmd_del(struct sk_buff *skb, struct genl_info *info) | 1005 | static int tcp_metrics_nl_cmd_del(struct sk_buff *skb, struct genl_info *info) |
| 961 | { | 1006 | { |
| 962 | struct tcpm_hash_bucket *hb; | 1007 | struct tcpm_hash_bucket *hb; |
| 963 | struct tcp_metrics_block *tm; | 1008 | struct tcp_metrics_block *tm, *tmlist = NULL; |
| 964 | struct tcp_metrics_block __rcu **pp; | 1009 | struct tcp_metrics_block __rcu **pp; |
| 965 | struct inetpeer_addr addr; | 1010 | struct inetpeer_addr saddr, daddr; |
| 966 | unsigned int hash; | 1011 | unsigned int hash; |
| 967 | struct net *net = genl_info_net(info); | 1012 | struct net *net = genl_info_net(info); |
| 968 | int ret; | 1013 | int ret; |
| 1014 | bool src = true; | ||
| 969 | 1015 | ||
| 970 | ret = parse_nl_addr(info, &addr, &hash, 1); | 1016 | ret = parse_nl_addr(info, &daddr, &hash, 1); |
| 971 | if (ret < 0) | 1017 | if (ret < 0) |
| 972 | return ret; | 1018 | return ret; |
| 973 | if (ret > 0) | 1019 | if (ret > 0) |
| 974 | return tcp_metrics_flush_all(net); | 1020 | return tcp_metrics_flush_all(net); |
| 1021 | ret = parse_nl_saddr(info, &saddr); | ||
| 1022 | if (ret < 0) | ||
| 1023 | src = false; | ||
| 975 | 1024 | ||
| 976 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); | 1025 | hash = hash_32(hash, net->ipv4.tcp_metrics_hash_log); |
| 977 | hb = net->ipv4.tcp_metrics_hash + hash; | 1026 | hb = net->ipv4.tcp_metrics_hash + hash; |
| 978 | pp = &hb->chain; | 1027 | pp = &hb->chain; |
| 979 | spin_lock_bh(&tcp_metrics_lock); | 1028 | spin_lock_bh(&tcp_metrics_lock); |
| 980 | for (tm = deref_locked_genl(*pp); tm; | 1029 | for (tm = deref_locked_genl(*pp); tm; tm = deref_locked_genl(*pp)) { |
| 981 | pp = &tm->tcpm_next, tm = deref_locked_genl(*pp)) { | 1030 | if (addr_same(&tm->tcpm_daddr, &daddr) && |
| 982 | if (addr_same(&tm->tcpm_addr, &addr)) { | 1031 | (!src || addr_same(&tm->tcpm_saddr, &saddr))) { |
| 983 | *pp = tm->tcpm_next; | 1032 | *pp = tm->tcpm_next; |
| 984 | break; | 1033 | tm->tcpm_next = tmlist; |
| 1034 | tmlist = tm; | ||
| 1035 | } else { | ||
| 1036 | pp = &tm->tcpm_next; | ||
| 985 | } | 1037 | } |
| 986 | } | 1038 | } |
| 987 | spin_unlock_bh(&tcp_metrics_lock); | 1039 | spin_unlock_bh(&tcp_metrics_lock); |
| 988 | if (!tm) | 1040 | if (!tmlist) |
| 989 | return -ESRCH; | 1041 | return -ESRCH; |
| 990 | kfree_rcu(tm, rcu_head); | 1042 | for (tm = tmlist; tm; tm = tmlist) { |
| 1043 | tmlist = tm->tcpm_next; | ||
| 1044 | kfree_rcu(tm, rcu_head); | ||
| 1045 | } | ||
| 991 | return 0; | 1046 | return 0; |
| 992 | } | 1047 | } |
| 993 | 1048 | ||
