Commit 13854e5a authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

inet: add proper refcounting to request sock

reqsk_put() is the generic function that should be used
to release a refcount (and automatically call reqsk_free())

reqsk_free() might be called if refcount is known to be 0
or undefined.

refcnt is set to one in inet_csk_reqsk_queue_add()

As request socks are not yet in global ehash table,
I added temporary debugging checks in reqsk_put() and reqsk_free()
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2c13270b
...@@ -275,6 +275,11 @@ static inline void inet_csk_reqsk_queue_add(struct sock *sk, ...@@ -275,6 +275,11 @@ static inline void inet_csk_reqsk_queue_add(struct sock *sk,
struct sock *child) struct sock *child)
{ {
reqsk_queue_add(&inet_csk(sk)->icsk_accept_queue, req, sk, child); reqsk_queue_add(&inet_csk(sk)->icsk_accept_queue, req, sk, child);
/* before letting lookups find us, make sure all req fields
* are committed to memory.
*/
smp_wmb();
atomic_set(&req->rsk_refcnt, 1);
} }
void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req, void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req,
......
...@@ -255,6 +255,11 @@ static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops ...@@ -255,6 +255,11 @@ static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops
ireq->opt = NULL; ireq->opt = NULL;
atomic64_set(&ireq->ir_cookie, 0); atomic64_set(&ireq->ir_cookie, 0);
ireq->ireq_state = TCP_NEW_SYN_RECV; ireq->ireq_state = TCP_NEW_SYN_RECV;
/* Following is temporary. It is coupled with debugging
* helpers in reqsk_put() & reqsk_free()
*/
atomic_set(&ireq->ireq_refcnt, 0);
} }
return req; return req;
......
...@@ -82,19 +82,20 @@ static inline struct request_sock *inet_reqsk(struct sock *sk) ...@@ -82,19 +82,20 @@ static inline struct request_sock *inet_reqsk(struct sock *sk)
return (struct request_sock *)sk; return (struct request_sock *)sk;
} }
static inline void __reqsk_free(struct request_sock *req)
{
kmem_cache_free(req->rsk_ops->slab, req);
}
static inline void reqsk_free(struct request_sock *req) static inline void reqsk_free(struct request_sock *req)
{ {
/* temporary debugging */
WARN_ON_ONCE(atomic_read(&req->rsk_refcnt) != 0);
req->rsk_ops->destructor(req); req->rsk_ops->destructor(req);
__reqsk_free(req); kmem_cache_free(req->rsk_ops->slab, req);
} }
static inline void reqsk_put(struct request_sock *req) static inline void reqsk_put(struct request_sock *req)
{ {
/* temporary debugging, until req sock are put into ehash table */
WARN_ON_ONCE(atomic_read(&req->rsk_refcnt) != 1);
if (atomic_dec_and_test(&req->rsk_refcnt)) if (atomic_dec_and_test(&req->rsk_refcnt))
reqsk_free(req); reqsk_free(req);
} }
......
...@@ -103,7 +103,7 @@ void reqsk_queue_destroy(struct request_sock_queue *queue) ...@@ -103,7 +103,7 @@ void reqsk_queue_destroy(struct request_sock_queue *queue)
while ((req = lopt->syn_table[i]) != NULL) { while ((req = lopt->syn_table[i]) != NULL) {
lopt->syn_table[i] = req->dl_next; lopt->syn_table[i] = req->dl_next;
lopt->qlen--; lopt->qlen--;
reqsk_free(req); reqsk_put(req);
} }
} }
} }
...@@ -180,7 +180,7 @@ void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req, ...@@ -180,7 +180,7 @@ void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req,
*/ */
spin_unlock_bh(&fastopenq->lock); spin_unlock_bh(&fastopenq->lock);
sock_put(lsk); sock_put(lsk);
reqsk_free(req); reqsk_put(req);
return; return;
} }
/* Wait for 60secs before removing a req that has triggered RST. /* Wait for 60secs before removing a req that has triggered RST.
......
...@@ -340,7 +340,7 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err) ...@@ -340,7 +340,7 @@ struct sock *inet_csk_accept(struct sock *sk, int flags, int *err)
out: out:
release_sock(sk); release_sock(sk);
if (req) if (req)
__reqsk_free(req); reqsk_put(req);
return newsk; return newsk;
out_err: out_err:
newsk = NULL; newsk = NULL;
...@@ -635,7 +635,7 @@ void inet_csk_reqsk_queue_prune(struct sock *parent, ...@@ -635,7 +635,7 @@ void inet_csk_reqsk_queue_prune(struct sock *parent,
/* Drop this request */ /* Drop this request */
inet_csk_reqsk_queue_unlink(parent, req, reqp); inet_csk_reqsk_queue_unlink(parent, req, reqp);
reqsk_queue_removed(queue, req); reqsk_queue_removed(queue, req);
reqsk_free(req); reqsk_put(req);
continue; continue;
} }
reqp = &req->dl_next; reqp = &req->dl_next;
...@@ -837,7 +837,7 @@ void inet_csk_listen_stop(struct sock *sk) ...@@ -837,7 +837,7 @@ void inet_csk_listen_stop(struct sock *sk)
sock_put(child); sock_put(child);
sk_acceptq_removed(sk); sk_acceptq_removed(sk);
__reqsk_free(req); reqsk_put(req);
} }
if (queue->fastopenq != NULL) { if (queue->fastopenq != NULL) {
/* Free all the reqs queued in rskq_rst_head. */ /* Free all the reqs queued in rskq_rst_head. */
...@@ -847,7 +847,7 @@ void inet_csk_listen_stop(struct sock *sk) ...@@ -847,7 +847,7 @@ void inet_csk_listen_stop(struct sock *sk)
spin_unlock_bh(&queue->fastopenq->lock); spin_unlock_bh(&queue->fastopenq->lock);
while ((req = acc_req) != NULL) { while ((req = acc_req) != NULL) {
acc_req = req->dl_next; acc_req = req->dl_next;
__reqsk_free(req); reqsk_put(req);
} }
} }
WARN_ON(sk->sk_ack_backlog); WARN_ON(sk->sk_ack_backlog);
......
...@@ -219,9 +219,9 @@ int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th, ...@@ -219,9 +219,9 @@ int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th,
} }
EXPORT_SYMBOL_GPL(__cookie_v4_check); EXPORT_SYMBOL_GPL(__cookie_v4_check);
static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, static struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb,
struct request_sock *req, struct request_sock *req,
struct dst_entry *dst) struct dst_entry *dst)
{ {
struct inet_connection_sock *icsk = inet_csk(sk); struct inet_connection_sock *icsk = inet_csk(sk);
struct sock *child; struct sock *child;
...@@ -357,7 +357,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) ...@@ -357,7 +357,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
ireq->opt = tcp_v4_save_options(skb); ireq->opt = tcp_v4_save_options(skb);
if (security_inet_conn_request(sk, skb, req)) { if (security_inet_conn_request(sk, skb, req)) {
reqsk_free(req); reqsk_put(req);
goto out; goto out;
} }
...@@ -378,7 +378,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) ...@@ -378,7 +378,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
security_req_classify_flow(req, flowi4_to_flowi(&fl4)); security_req_classify_flow(req, flowi4_to_flowi(&fl4));
rt = ip_route_output_key(sock_net(sk), &fl4); rt = ip_route_output_key(sock_net(sk), &fl4);
if (IS_ERR(rt)) { if (IS_ERR(rt)) {
reqsk_free(req); reqsk_put(req);
goto out; goto out;
} }
......
...@@ -253,7 +253,7 @@ static bool tcp_fastopen_queue_check(struct sock *sk) ...@@ -253,7 +253,7 @@ static bool tcp_fastopen_queue_check(struct sock *sk)
fastopenq->rskq_rst_head = req1->dl_next; fastopenq->rskq_rst_head = req1->dl_next;
fastopenq->qlen--; fastopenq->qlen--;
spin_unlock(&fastopenq->lock); spin_unlock(&fastopenq->lock);
reqsk_free(req1); reqsk_put(req1);
} }
return true; return true;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment