diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2016-08-19 04:21:37 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-08-19 17:40:25 -0400 |
commit | ad202074320cd75b31b8cdb58cca0d4ef6aaea8a (patch) | |
tree | 2a64a15410e84838e189a8324e0a0b3ae6f5bda9 | |
parent | 39ec406d122be703ee51cb2754fe18b1f7037fa0 (diff) |
netlink: Use rhashtable walk interface in diag dump
This patch converts the diag dumping code to use the rhashtable
walk code instead of going through rhashtable by hand. The lock
nl_table_lock is now only taken while we process the multicast
list as it's not needed for the rhashtable walk.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/netlink/diag.c | 103 |
1 files changed, 73 insertions, 30 deletions
diff --git a/net/netlink/diag.c b/net/netlink/diag.c index 8dd836a8dd60..3e3e2534478a 100644 --- a/net/netlink/diag.c +++ b/net/netlink/diag.c | |||
@@ -63,43 +63,75 @@ out_nlmsg_trim: | |||
63 | static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, | 63 | static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, |
64 | int protocol, int s_num) | 64 | int protocol, int s_num) |
65 | { | 65 | { |
66 | struct rhashtable_iter *hti = (void *)cb->args[2]; | ||
66 | struct netlink_table *tbl = &nl_table[protocol]; | 67 | struct netlink_table *tbl = &nl_table[protocol]; |
67 | struct rhashtable *ht = &tbl->hash; | ||
68 | const struct bucket_table *htbl = rht_dereference_rcu(ht->tbl, ht); | ||
69 | struct net *net = sock_net(skb->sk); | 68 | struct net *net = sock_net(skb->sk); |
70 | struct netlink_diag_req *req; | 69 | struct netlink_diag_req *req; |
71 | struct netlink_sock *nlsk; | 70 | struct netlink_sock *nlsk; |
72 | struct sock *sk; | 71 | struct sock *sk; |
73 | int ret = 0, num = 0, i; | 72 | int num = 2; |
73 | int ret = 0; | ||
74 | 74 | ||
75 | req = nlmsg_data(cb->nlh); | 75 | req = nlmsg_data(cb->nlh); |
76 | 76 | ||
77 | for (i = 0; i < htbl->size; i++) { | 77 | if (s_num > 1) |
78 | struct rhash_head *pos; | 78 | goto mc_list; |
79 | 79 | ||
80 | rht_for_each_entry_rcu(nlsk, pos, htbl, i, node) { | 80 | num--; |
81 | sk = (struct sock *)nlsk; | ||
82 | 81 | ||
83 | if (!net_eq(sock_net(sk), net)) | 82 | if (!hti) { |
84 | continue; | 83 | hti = kmalloc(sizeof(*hti), GFP_KERNEL); |
85 | if (num < s_num) { | 84 | if (!hti) |
86 | num++; | 85 | return -ENOMEM; |
86 | |||
87 | cb->args[2] = (long)hti; | ||
88 | } | ||
89 | |||
90 | if (!s_num) | ||
91 | rhashtable_walk_enter(&tbl->hash, hti); | ||
92 | |||
93 | ret = rhashtable_walk_start(hti); | ||
94 | if (ret == -EAGAIN) | ||
95 | ret = 0; | ||
96 | if (ret) | ||
97 | goto stop; | ||
98 | |||
99 | while ((nlsk = rhashtable_walk_next(hti))) { | ||
100 | if (IS_ERR(nlsk)) { | ||
101 | ret = PTR_ERR(nlsk); | ||
102 | if (ret == -EAGAIN) { | ||
103 | ret = 0; | ||
87 | continue; | 104 | continue; |
88 | } | 105 | } |
106 | break; | ||
107 | } | ||
89 | 108 | ||
90 | if (sk_diag_fill(sk, skb, req, | 109 | sk = (struct sock *)nlsk; |
91 | NETLINK_CB(cb->skb).portid, | ||
92 | cb->nlh->nlmsg_seq, | ||
93 | NLM_F_MULTI, | ||
94 | sock_i_ino(sk)) < 0) { | ||
95 | ret = 1; | ||
96 | goto done; | ||
97 | } | ||
98 | 110 | ||
99 | num++; | 111 | if (!net_eq(sock_net(sk), net)) |
112 | continue; | ||
113 | |||
114 | if (sk_diag_fill(sk, skb, req, | ||
115 | NETLINK_CB(cb->skb).portid, | ||
116 | cb->nlh->nlmsg_seq, | ||
117 | NLM_F_MULTI, | ||
118 | sock_i_ino(sk)) < 0) { | ||
119 | ret = 1; | ||
120 | break; | ||
100 | } | 121 | } |
101 | } | 122 | } |
102 | 123 | ||
124 | stop: | ||
125 | rhashtable_walk_stop(hti); | ||
126 | if (ret) | ||
127 | goto done; | ||
128 | |||
129 | rhashtable_walk_exit(hti); | ||
130 | cb->args[2] = 0; | ||
131 | num++; | ||
132 | |||
133 | mc_list: | ||
134 | read_lock(&nl_table_lock); | ||
103 | sk_for_each_bound(sk, &tbl->mc_list) { | 135 | sk_for_each_bound(sk, &tbl->mc_list) { |
104 | if (sk_hashed(sk)) | 136 | if (sk_hashed(sk)) |
105 | continue; | 137 | continue; |
@@ -116,13 +148,14 @@ static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, | |||
116 | NLM_F_MULTI, | 148 | NLM_F_MULTI, |
117 | sock_i_ino(sk)) < 0) { | 149 | sock_i_ino(sk)) < 0) { |
118 | ret = 1; | 150 | ret = 1; |
119 | goto done; | 151 | break; |
120 | } | 152 | } |
121 | num++; | 153 | num++; |
122 | } | 154 | } |
155 | read_unlock(&nl_table_lock); | ||
156 | |||
123 | done: | 157 | done: |
124 | cb->args[0] = num; | 158 | cb->args[0] = num; |
125 | cb->args[1] = protocol; | ||
126 | 159 | ||
127 | return ret; | 160 | return ret; |
128 | } | 161 | } |
@@ -131,20 +164,20 @@ static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
131 | { | 164 | { |
132 | struct netlink_diag_req *req; | 165 | struct netlink_diag_req *req; |
133 | int s_num = cb->args[0]; | 166 | int s_num = cb->args[0]; |
167 | int err = 0; | ||
134 | 168 | ||
135 | req = nlmsg_data(cb->nlh); | 169 | req = nlmsg_data(cb->nlh); |
136 | 170 | ||
137 | rcu_read_lock(); | ||
138 | read_lock(&nl_table_lock); | ||
139 | |||
140 | if (req->sdiag_protocol == NDIAG_PROTO_ALL) { | 171 | if (req->sdiag_protocol == NDIAG_PROTO_ALL) { |
141 | int i; | 172 | int i; |
142 | 173 | ||
143 | for (i = cb->args[1]; i < MAX_LINKS; i++) { | 174 | for (i = cb->args[1]; i < MAX_LINKS; i++) { |
144 | if (__netlink_diag_dump(skb, cb, i, s_num)) | 175 | err = __netlink_diag_dump(skb, cb, i, s_num); |
176 | if (err) | ||
145 | break; | 177 | break; |
146 | s_num = 0; | 178 | s_num = 0; |
147 | } | 179 | } |
180 | cb->args[1] = i; | ||
148 | } else { | 181 | } else { |
149 | if (req->sdiag_protocol >= MAX_LINKS) { | 182 | if (req->sdiag_protocol >= MAX_LINKS) { |
150 | read_unlock(&nl_table_lock); | 183 | read_unlock(&nl_table_lock); |
@@ -152,13 +185,22 @@ static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
152 | return -ENOENT; | 185 | return -ENOENT; |
153 | } | 186 | } |
154 | 187 | ||
155 | __netlink_diag_dump(skb, cb, req->sdiag_protocol, s_num); | 188 | err = __netlink_diag_dump(skb, cb, req->sdiag_protocol, s_num); |
156 | } | 189 | } |
157 | 190 | ||
158 | read_unlock(&nl_table_lock); | 191 | return err < 0 ? err : skb->len; |
159 | rcu_read_unlock(); | 192 | } |
193 | |||
194 | static int netlink_diag_dump_done(struct netlink_callback *cb) | ||
195 | { | ||
196 | struct rhashtable_iter *hti = (void *)cb->args[2]; | ||
197 | |||
198 | if (cb->args[0] == 1) | ||
199 | rhashtable_walk_exit(hti); | ||
160 | 200 | ||
161 | return skb->len; | 201 | kfree(hti); |
202 | |||
203 | return 0; | ||
162 | } | 204 | } |
163 | 205 | ||
164 | static int netlink_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) | 206 | static int netlink_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) |
@@ -172,6 +214,7 @@ static int netlink_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) | |||
172 | if (h->nlmsg_flags & NLM_F_DUMP) { | 214 | if (h->nlmsg_flags & NLM_F_DUMP) { |
173 | struct netlink_dump_control c = { | 215 | struct netlink_dump_control c = { |
174 | .dump = netlink_diag_dump, | 216 | .dump = netlink_diag_dump, |
217 | .done = netlink_diag_dump_done, | ||
175 | }; | 218 | }; |
176 | return netlink_dump_start(net->diag_nlsk, skb, h, &c); | 219 | return netlink_dump_start(net->diag_nlsk, skb, h, &c); |
177 | } else | 220 | } else |