Zapping pages is required only if we are calling vm_insert_page into a region where pages had previously been mapped. Receive zerocopy allows reusing such regions, and hitherto called zap_page_range() before calling vm_insert_page() in that range. zap_page_range() can also be triggered from userspace with madvise(MADV_DONTNEED). If userspace is configured to call this before reusing a segment, or if there was nothing mapped at this virtual address to begin with, we can avoid calling zap_page_range() under the socket lock. That said, if userspace does not do that, then we are still responsible for calling zap_page_range(). This patch adds a flag that the user can use to hint to the kernel that a zap is not required. If the flag is not set, or if an older user application does not have a flags field at all, then the kernel calls zap_page_range as before. Also, if the flag is set but a zap is still required, the kernel performs that zap as necessary. Thus incorrectly indicating that a zap can be avoided does not change the correctness of operation. It also increases the batchsize for vm_insert_pages and prefetches the page struct for the batch since we're about to bump the refcount. An alternative mechanism could be to not have a flag, assume by default a zap is not needed, and fall back to zapping if needed. However, this would harm performance for older applications for which a zap is necessary, and thus we implement it with an explicit flag so newer applications can opt in. When using RPC-style traffic with medium sized (tens of KB) RPCs, this change yields an efficency improvement of about 30% for QPS/CPU usage.
Showing the changes to include/uapi/linux (1 of 2 files). Show all
Unified · Split
| @@ -343,6 +343,7 @@ struct tcp_diag_md5sig { · 342 unmodified lines | ||
| 343 | 343 | |
| 344 | 344 | /* setsockopt(fd, IPPROTO_TCP, TCP_ZEROCOPY_RECEIVE, ...) */ |
| 345 | 345 | |
| 346 | +#define TCP_RECEIVE_ZEROCOPY_FLAG_TLB_CLEAN_HINT 0x1 | |
| 346 | 347 | struct tcp_zerocopy_receive { |
| 347 | 348 | __u64 address; /* in: address of mapping */ |
| 348 | 349 | __u32 length; /* in/out: number of bytes to map/mapped */ |
| @@ -351,5 +352,6 @@ struct tcp_zerocopy_receive { · 2 unmodified lines | ||
| 351 | 352 | __s32 err; /* out: socket error */ |
| 352 | 353 | __u64 copybuf_address; /* in: copybuf address (small reads) */ |
| 353 | 354 | __s32 copybuf_len; /* in/out: copybuf bytes avail/used or error */ |
| 355 | + __u32 flags; /* in: flags */ | |
| 354 | 356 | }; |
| 355 | 357 | #endif /* _UAPI_LINUX_TCP_H */ |