diff options
| author | Paulo Alcantara <pc@manguebit.org> | 2026-09-26 19:20:30 -0300 |
|---|---|---|
| committer | Paulo Alcantara <pc@manguebit.org> | 2026-09-30 14:24:12 -0300 |
| commit | dd05add7b2b6004c5fa3a1f276f56d8668f86b65 (patch) | |
| tree | 23efdcaa0b8031f6ac6f57b6734d18d6c1adc6f2 /include/net/request_sock.h | |
| download | linux-stable-dd05add7b2b6004c5fa3a1f276f56d8668f86b65.tar.gz linux-stable-dd05add7b2b6004c5fa3a1f276f56d8668f86b65.zip | |
netfs: zero the tail of a short DIO/unbuffered readgrafted
The buffered read collector zero-fills the tail of a short read that
stops below the inode's i_size (netfs_clear_unread()), so a read that
races an extending write still returns the expected number of bytes.
The non-buffered collector path does no such thing: it just records
how much was transferred.
Add the same zero-fill for the non-buffered case, gated on
NETFS_SREQ_CLEAR_TAIL: a subreq's source sets that flag when a short
result from it is known to be safe to treat as a hole, as opposed to
NETFS_SREQ_HIT_EOF, which means the read genuinely ran off the end of
the file and should be reported short as-is. Only CLEAR_TAIL should
zero-fill here; a real EOF must stay a real short read.
No source currently sets CLEAR_TAIL on an unbuffered/DIO subrequest,
so this is inert on its own -- a following change teaches cifs to set
it in the one case that needs it.
Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item")
Reviewed-by: David Howells <dhowells@redhat.com>
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Paulo Alcantara <pc@manguebit.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Ronnie Sahlberg <ronniesahlberg@gmail.com>
Cc: Shyam Prasad N <sprasad@microsoft.com>
Cc: Tom Talpey <tom@talpey.com>
Cc: Bharath SM <bharathsm@microsoft.com>
Cc: stable@vger.kernel.org
Diffstat (limited to 'include/net/request_sock.h')
| -rw-r--r-- | include/net/request_sock.h | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/include/net/request_sock.h b/include/net/request_sock.h new file mode 100644 index 000000000..5a9c826a7 --- /dev/null +++ b/include/net/request_sock.h @@ -0,0 +1,253 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * NET Generic infrastructure for Network protocols. + * + * Definitions for request_sock + * + * Authors: Arnaldo Carvalho de Melo <acme@conectiva.com.br> + * + * From code originally in include/net/tcp.h + */ +#ifndef _REQUEST_SOCK_H +#define _REQUEST_SOCK_H + +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/bug.h> +#include <linux/refcount.h> + +#include <net/sock.h> +#include <net/rstreason.h> + +struct request_sock; +struct sk_buff; +struct dst_entry; +struct proto; + +struct request_sock_ops { + int family; + unsigned int obj_size; + struct kmem_cache *slab; + char *slab_name; + void (*send_ack)(const struct sock *sk, struct sk_buff *skb, + struct request_sock *req); + void (*send_reset)(const struct sock *sk, + struct sk_buff *skb, + enum sk_rst_reason reason); + void (*destructor)(struct request_sock *req); +}; + +struct saved_syn { + u32 mac_hdrlen; + u32 network_hdrlen; + u32 tcp_hdrlen; + u8 data[]; +}; + +/* struct request_sock - mini sock to represent a connection request + */ +struct request_sock { + struct sock_common __req_common; +#define rsk_refcnt __req_common.skc_refcnt +#define rsk_hash __req_common.skc_hash +#define rsk_listener __req_common.skc_listener +#define rsk_window_clamp __req_common.skc_window_clamp +#define rsk_rcv_wnd __req_common.skc_rcv_wnd + + struct request_sock *dl_next; + u16 mss; + u8 num_retrans; /* number of retransmits */ + u8 syncookie:1; /* True if + * 1) tcpopts needs to be encoded in + * TS of SYN+ACK + * 2) ACK is validated by BPF kfunc. + */ + u8 num_timeout:7; /* number of timeouts */ + u32 ts_recent; + struct timer_list rsk_timer; + const struct request_sock_ops *rsk_ops; + struct sock *sk; + struct saved_syn *saved_syn; + u32 secid; + u32 peer_secid; + u32 timeout; +}; + +static inline struct request_sock *inet_reqsk(const struct sock *sk) +{ + return (struct request_sock *)sk; +} + +static inline struct sock *req_to_sk(struct request_sock *req) +{ + return (struct sock *)req; +} + +/** + * skb_steal_sock - steal a socket from an sk_buff + * @skb: sk_buff to steal the socket from + * @refcounted: is set to true if the socket is reference-counted + * @prefetched: is set to true if the socket was assigned from bpf + */ +static inline struct sock *skb_steal_sock(struct sk_buff *skb, + bool *refcounted, bool *prefetched) +{ + struct sock *sk = skb->sk; + + if (!sk) { + *prefetched = false; + *refcounted = false; + return NULL; + } + + *prefetched = skb_sk_is_prefetched(skb); + if (*prefetched) { +#if IS_ENABLED(CONFIG_SYN_COOKIES) + if (sk->sk_state == TCP_NEW_SYN_RECV && inet_reqsk(sk)->syncookie) { + struct request_sock *req = inet_reqsk(sk); + + *refcounted = false; + sk = req->rsk_listener; + req->rsk_listener = NULL; + return sk; + } +#endif + *refcounted = sk_is_refcounted(sk); + } else { + *refcounted = true; + } + + skb->destructor = NULL; + skb->sk = NULL; + return sk; +} + +void __reqsk_free(struct request_sock *req); + +static inline void reqsk_free(struct request_sock *req) +{ + DEBUG_NET_WARN_ON_ONCE(refcount_read(&req->rsk_refcnt) != 0); + __reqsk_free(req); +} + +static inline void reqsk_put(struct request_sock *req) +{ + if (refcount_dec_and_test(&req->rsk_refcnt)) + __reqsk_free(req); +} + +/* + * For a TCP Fast Open listener - + * lock - protects the access to all the reqsk, which is co-owned by + * the listener and the child socket. + * qlen - pending TFO requests (still in TCP_SYN_RECV). + * max_qlen - max TFO reqs allowed before TFO is disabled. + * + * XXX (TFO) - ideally these fields can be made as part of "listen_sock" + * structure above. But there is some implementation difficulty due to + * listen_sock being part of request_sock_queue hence will be freed when + * a listener is stopped. But TFO related fields may continue to be + * accessed even after a listener is closed, until its sk_refcnt drops + * to 0 implying no more outstanding TFO reqs. One solution is to keep + * listen_opt around until sk_refcnt drops to 0. But there is some other + * complexity that needs to be resolved. E.g., a listener can be disabled + * temporarily through shutdown()->tcp_disconnect(), and re-enabled later. + */ +struct fastopen_queue { + struct request_sock *rskq_rst_head; /* Keep track of past TFO */ + struct request_sock *rskq_rst_tail; /* requests that caused RST. + * This is part of the defense + * against spoofing attack. + */ + spinlock_t lock; + int qlen; /* # of pending (TCP_SYN_RECV) reqs */ + int max_qlen; /* != 0 iff TFO is currently enabled */ + + struct tcp_fastopen_context __rcu *ctx; /* cipher context for cookie */ +}; + +/** struct request_sock_queue - queue of request_socks + * + * @rskq_accept_head - FIFO head of established children + * @rskq_accept_tail - FIFO tail of established children + * @rskq_defer_accept - User waits for some data after accept() + * + */ +struct request_sock_queue { + spinlock_t rskq_lock; + u8 rskq_defer_accept; + u8 synflood_warned; + + atomic_t qlen; + atomic_t young; + + struct request_sock *rskq_accept_head; + struct request_sock *rskq_accept_tail; + struct fastopen_queue fastopenq; /* Check max_qlen != 0 to determine + * if TFO is enabled. + */ +}; + +void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req, + bool reset); + +static inline bool reqsk_queue_empty(const struct request_sock_queue *queue) +{ + return READ_ONCE(queue->rskq_accept_head) == NULL; +} + +static inline struct request_sock *reqsk_queue_remove(struct request_sock_queue *queue, + struct sock *parent) +{ + struct request_sock *req; + + spin_lock_bh(&queue->rskq_lock); + req = queue->rskq_accept_head; + if (req) { + sk_acceptq_removed(parent); + WRITE_ONCE(queue->rskq_accept_head, req->dl_next); + if (queue->rskq_accept_head == NULL) + queue->rskq_accept_tail = NULL; + } + spin_unlock_bh(&queue->rskq_lock); + return req; +} + +static inline void reqsk_queue_removed(struct request_sock_queue *queue, + const struct request_sock *req) +{ + if (req->num_timeout == 0) + atomic_dec(&queue->young); + atomic_dec(&queue->qlen); +} + +static inline void reqsk_queue_added(struct request_sock_queue *queue) +{ + atomic_inc(&queue->young); + atomic_inc(&queue->qlen); +} + +static inline int reqsk_queue_len(const struct request_sock_queue *queue) +{ + return atomic_read(&queue->qlen); +} + +static inline int reqsk_queue_len_young(const struct request_sock_queue *queue) +{ + return atomic_read(&queue->young); +} + +/* RFC 7323 2.3 Using the Window Scale Option + * The window field (SEG.WND) of every outgoing segment, with the + * exception of <SYN> segments, MUST be right-shifted by + * Rcv.Wind.Shift bits. + * + * This means the SEG.WND carried in SYNACK can not exceed 65535. + * We use this property to harden TCP stack while in NEW_SYN_RECV state. + */ +static inline u32 tcp_synack_window(const struct request_sock *req) +{ + return min(req->rsk_rcv_wnd, 65535U); +} +#endif /* _REQUEST_SOCK_H */ |
