: shrink struct sock
Subsystems (3)
PATCH 1/1 : shrink struct sock
Hi David,
This saves 8 bytes out of struct sock in 64bit arches, tested on x86_64.
[acme@mica linux-2.6]$ codiff sock.o.before net/core/sock.o /home/acme/git/linux-2.6/net/core/sock.c:
struct sock | -8
1 struct changed
[acme@mica linux-2.6]$
Now struct sock has this layout, where we waste more 20 bytes, in four 4 byte paddings in struct skb_buff_head members (layout at the bottom) + one 4 bytes hole:
[acme@mica linux-2.6]$ pahole -C sock net/core/sock.o struct sock {
struct sock_common __sk_common; /* 0 56 */ unsigned char sk_shutdown:2; /* 56 1 */ unsigned char sk_no_check:2; /* 56 1 */ unsigned char sk_userlocks:4; /* 56 1 */ unsigned char sk_protocol; /* 57 1 */ short unsigned int sk_type; /* 58 2 */ int sk_rcvbuf; /* 60 4 */
/* --- cacheline 1 boundary (64 bytes) --- */
socket_lock_t sk_lock; /* 64 40 */
struct {
struct sk_buff * head; /* 104 8 */
struct sk_buff * tail; /* 112 8 */
} sk_backlog; /* 104 16 */
wait_queue_head_t * sk_sleep; /* 120 8 */
/* --- cacheline 2 boundary (128 bytes) --- */
struct dst_entry * sk_dst_cache; /* 128 8 */
struct xfrm_policy * sk_policy[2]; /* 136 16 */
rwlock_t sk_dst_lock; /* 152 8 */
atomic_t sk_rmem_alloc; /* 160 4 */
atomic_t sk_wmem_alloc; /* 164 4 */
atomic_t sk_omem_alloc; /* 168 4 */
int sk_sndbuf; /* 172 4 */
struct sk_buff_head sk_receive_queue; /* 176 32 */
/* XXX last struct has 4 bytes of padding */
/* --- cacheline 3 boundary (192 bytes) was 16 bytes ago --- */
struct sk_buff_head sk_write_queue; /* 208 32 */
/* XXX last struct has 4 bytes of padding */
struct sk_buff_head sk_async_wait_queue; /* 240 32 */
/* XXX last struct has 4 bytes of padding */
/* --- cacheline 4 boundary (256 bytes) was 16 bytes ago --- */
int sk_wmem_queued; /* 272 4 */
int sk_forward_alloc; /* 276 4 */
gfp_t sk_allocation; /* 280 4 */
int sk_route_caps; /* 284 4 */
int sk_gso_type; /* 288 4 */
int sk_rcvlowat; /* 292 4 */
long unsigned int sk_flags; /* 296 8 */
long unsigned int sk_lingertime; /* 304 8 */
struct sk_buff_head sk_error_queue; /* 312 32 */
/* XXX last struct has 4 bytes of padding */
/* --- cacheline 5 boundary (320 bytes) was 24 bytes ago --- */
struct proto * sk_prot_creator; /* 344 8 */
rwlock_t sk_callback_lock; /* 352 8 */
int sk_err; /* 360 4 */
int sk_err_soft; /* 364 4 */
short unsigned int sk_ack_backlog; /* 368 2 */
short unsigned int sk_max_ack_backlog; /* 370 2 */
__u32 sk_priority; /* 372 4 */
struct ucred sk_peercred; /* 376 12 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 6 boundary (384 bytes) was 8 bytes ago --- */
long int sk_rcvtimeo; /* 392 8 */
long int sk_sndtimeo; /* 400 8 */
struct sk_filter * sk_filter; /* 408 8 */
void * sk_protinfo; /* 416 8 */
struct timer_list sk_timer; /* 424 48 */
/* --- cacheline 7 boundary (448 bytes) was 24 bytes ago --- */
ktime_t sk_stamp; /* 472 8 */
struct socket * sk_socket; /* 480 8 */
void * sk_user_data; /* 488 8 */
struct page * sk_sndmsg_page; /* 496 8 */
struct sk_buff * sk_send_head; /* 504 8 */
/* --- cacheline 8 boundary (512 bytes) --- */
__u32 sk_sndmsg_off; /* 512 4 */
int sk_write_pending; /* 516 4 */
void * sk_security; /* 520 8 */
void (*sk_state_change)(struct sock *); /* 528 8 */
void (*sk_data_ready)(struct sock *, int); /* 536 8 */
void (*sk_write_space)(struct sock *); /* 544 8 */
void (*sk_error_report)(struct sock *); /* 552 8 */
int (*sk_backlog_rcv)(struct sock *, struct sk_buff *); /* 560 8 */
void (*sk_destruct)(struct sock *); /* 568 8 */
/* --- cacheline 9 boundary (576 bytes) --- */
}; /* size: 576, cachelines: 9 */
/* sum members: 572, holes: 1, sum holes: 4 */
/* paddings: 4, sum paddings: 16 */
[acme@mica linux-2.6]$ pahole -C sk_buff_head net/core/sock.o struct sk_buff_head {
struct sk_buff * next; /* 0 8 */ struct sk_buff * prev; /* 8 8 */ __u32 qlen; /* 16 4 */ spinlock_t lock; /* 20 8 */
}; /* size: 32, cachelines: 1 */
/* padding: 4 */
/* last cacheline: 32 bytes */
---
include/net/sock.h +1 -1
@@ -218,13 +218,13 @@ struct sock {atomic_t sk_rmem_alloc;atomic_t sk_wmem_alloc;atomic_t sk_omem_alloc; +int sk_sndbuf;struct sk_buff_head sk_receive_queue;struct sk_buff_head sk_write_queue;struct sk_buff_head sk_async_wait_queue;int sk_wmem_queued;int sk_forward_alloc;gfp_t sk_allocation; -int sk_sndbuf;int sk_route_caps;int sk_gso_type;int sk_rcvlowat;
David Miller
maintainerFrom: Arnaldo Carvalho de Melo <[email protected]>
Date: Fri, 4 May 2007 01:11:01 -0300
Quoted 2 lines
This saves 8 bytes out of struct sock in 64bit arches, tested on
x86_64.
Thanks I'll apply this.
Quoted 3 lines
Now struct sock has this layout, where we waste more 20 bytes, in four 4 byte paddings in struct
skb_buff_headmembers (layout at the bottom) + one 4 bytes hole:
sk_buff_head is due for being killed from the whole tree. Nobody really needs the qlen, few things really need the lock, and those that do can define their own as needed :-)
There are also cases where we don't even need to dual-linkage so could decrease it down to one pointer.
Quoted 4 lines · David Miller
sk_buff_headis due for being killed from the whole tree. Nobody really needs the qlen, few things really need the lock, and those that do can define their own as needed :-)
I've got out of tree research code that uses the qlen quite significantly. However it's not high speed networking so can compute it myself if needed...
Signature · 3 lines
Web: http://wand.net.nz/~iam4/ Blog: http://iansblog.jandi.co.nz WAND Network Research Group
Quoted 10 lines · Arnaldo Carvalho de Melo
[acme@mica linux-2.6]$ pahole -C
sk_buff_headnet/core/sock.ostructsk_buff_head{struct sk_buff * next; /* 0 8 */ struct sk_buff * prev; /* 8 8 */ __u32 qlen; /* 16 4 */ spinlock_t lock; /* 20 8 */}; /* size: 32, cachelines: 1 */
/* padding: 4 */
/* last cacheline: 32 bytes */
Hum, maybe not enough tea this morning, but I always thought spinlock_t size was 4 bytes....
Quoted 13 lines · Eric Dumazet
Quoted 10 lines · Arnaldo Carvalho de Melo
[acme@mica linux-2.6]$ pahole -C
sk_buff_headnet/core/sock.ostructsk_buff_head{struct sk_buff * next; /* 0 8 */ struct sk_buff * prev; /* 8 8 */ __u32 qlen; /* 16 4 */ spinlock_t lock; /* 20 8 */}; /* size: 32, cachelines: 1 */
/* padding: 4 */
/* last cacheline: 32 bytes */
Quoted 2 lines
Hum, maybe not enough tea this morning, but I always thought
spinlock_tsize was 4 bytes....
Well, unfolding sk_buff_head to see the details:
[acme@mica linux-2.6]$ pahole --expand_types -C sk_buff_head net/core/sock.o /* <9a61> /home/acme/git/linux-2.6/include/linux/skbuff.h:111 */
struct sk_buff_head {
struct sk_buff * next; /* 0 8 */
struct sk_buff * prev; /* 8 8 */
/* typedef __u32 */ unsigned int qlen; /* 16 4 */
/* typedef spinlock_t */ struct {
/* typedef raw_spinlock_t */ struct {
unsigned int slock; /* 20 4 */
} raw_lock; /* 20 4 */
unsigned int break_lock; /* 24 4 */
} lock; /* 20 8 */
}; /* size: 32, cachelines: 1 */ /* padding: 4 */
/* last cacheline: 32 bytes */
#if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP)
unsigned int break_lock;#endif
Disabling CONFIG_PREEMPT makes you regain your confidence in the quality of your tea:
[acme@mica linux-2.6]$ pahole --expand_types -C sk_buff_head net/core/sock.o /* <9a40> /home/acme/git/linux-2.6/include/linux/skbuff.h:111 */
struct sk_buff_head {
struct sk_buff * next; /* 0 8 */
struct sk_buff * prev; /* 8 8 */
/* typedef __u32 */ unsigned int qlen; /* 16 4 */
/* typedef spinlock_t */ struct {
/* typedef raw_spinlock_t */ struct {
unsigned int slock; /* 20 4 */
} raw_lock; /* 20 4 */
} lock; /* 20 4 */
}; /* size: 24, cachelines: 1 *//* last cacheline: 24 bytes */
And then struct sock gets leaner too:
[acme@mica linux-2.6]$ pahole --expand_types -C sock net/core/sock.o /* <7a57> /home/acme/git/linux-2.6/include/linux/net.h:114 */
struct sock {
struct sock_common {
short unsigned int skc_family; /* 0 2 */
volatile unsigned char skc_state; /* 2 1 */
unsigned char skc_reuse; /* 3 1 */
int skc_bound_dev_if; /* 4 4 */
struct hlist_node {
struct hlist_node * next; /* 8 8 */
struct hlist_node * * pprev; /* 16 8 */
} skc_node; /* 8 16 */
struct hlist_node {
struct hlist_node * next; /* 24 8 */
struct hlist_node * * pprev; /* 32 8 */
} skc_bind_node; /* 24 16 */
/* typedef atomic_t */ struct {
int counter; /* 40 4 */
} skc_refcnt; /* 40 4 */
unsigned int skc_hash; /* 44 4 */
struct proto * skc_prot; /* 48 8 */
} __sk_common; /* 0 56 */
unsigned char sk_shutdown:2; /* 56 1 */
unsigned char sk_no_check:2; /* 56 1 */
unsigned char sk_userlocks:4; /* 56 1 */
unsigned char sk_protocol; /* 57 1 */
short unsigned int sk_type; /* 58 2 */
int sk_rcvbuf; /* 60 4 */
/* --- cacheline 1 boundary (64 bytes) --- */
/* typedef socket_lock_t */ struct {
/* typedef spinlock_t */ struct {
/* typedef raw_spinlock_t */ struct {
unsigned int slock; /* 64 4 */
} raw_lock; /* 64 4 */
} slock; /* 64 4 */
struct sock_iocb * owner; /* 72 8 */
/* typedef wait_queue_head_t */ struct __wait_queue_head {
/* typedef spinlock_t */ struct {
/* typedef raw_spinlock_t */ struct {
unsigned int slock; /* 80 4 */
} raw_lock; /* 80 4 */
} lock; /* 80 4 */
/* XXX 4 bytes hole, try to pack */
struct list_head {
struct list_head * next; /* 88 8 */
struct list_head * prev; /* 96 8 */
} task_list; /* 88 16 */
} wq; /* 80 24 */
} sk_lock; /* 64 40 */
struct {
struct sk_buff * head; /* 104 8 */
struct sk_buff * tail; /* 112 8 */
} sk_backlog; /* 104 16 */
wait_queue_head_t * sk_sleep; /* 120 8 */
/* --- cacheline 2 boundary (128 bytes) --- */
struct dst_entry * sk_dst_cache; /* 128 8 */
struct xfrm_policy * sk_policy[2]; /* 136 16 */
/* typedef rwlock_t */ struct {
/* typedef raw_rwlock_t */ struct {
unsigned int lock; /* 152 4 */
} raw_lock; /* 152 4 */
} sk_dst_lock; /* 152 4 */
/* typedef atomic_t */ struct {
int counter; /* 156 4 */
} sk_rmem_alloc; /* 156 4 */
/* typedef atomic_t */ struct {
int counter; /* 160 4 */
} sk_wmem_alloc; /* 160 4 */
/* typedef atomic_t */ struct {
int counter; /* 164 4 */
} sk_omem_alloc; /* 164 4 */
int sk_sndbuf; /* 168 4 */
/* XXX 4 bytes hole, try to pack */
struct sk_buff_head {
struct sk_buff * next; /* 176 8 */
struct sk_buff * prev; /* 184 8 */
/* typedef __u32 */ unsigned int qlen; /* 192 4 */
/* typedef spinlock_t */ struct {
/* typedef raw_spinlock_t */ struct {
unsigned int slock; /* 196 4 */
} raw_lock; /* 196 4 */
} lock; /* 196 4 */
} sk_receive_queue; /* 176 24 */
/* --- cacheline 3 boundary (192 bytes) was 8 bytes ago --- */
struct sk_buff_head {
struct sk_buff * next; /* 200 8 */
struct sk_buff * prev; /* 208 8 */
/* typedef __u32 */ unsigned int qlen; /* 216 4 */
/* typedef spinlock_t */ struct {
/* typedef raw_spinlock_t */ struct {
unsigned int slock; /* 220 4 */
} raw_lock; /* 220 4 */
} lock; /* 220 4 */
} sk_write_queue; /* 200 24 */
struct sk_buff_head {
struct sk_buff * next; /* 224 8 */
struct sk_buff * prev; /* 232 8 */
/* typedef __u32 */ unsigned int qlen; /* 240 4 */
/* typedef spinlock_t */ struct {
/* typedef raw_spinlock_t */ struct {
unsigned int slock; /* 244 4 */
} raw_lock; /* 244 4 */
} lock; /* 244 4 */
} sk_async_wait_queue; /* 224 24 */
int sk_wmem_queued; /* 248 4 */
int sk_forward_alloc; /* 252 4 */
/* --- cacheline 4 boundary (256 bytes) --- */
/* typedef gfp_t */ unsigned int sk_allocation; /* 256 4 */
int sk_route_caps; /* 260 4 */
int sk_gso_type; /* 264 4 */
int sk_rcvlowat; /* 268 4 */
long unsigned int sk_flags; /* 272 8 */
long unsigned int sk_lingertime; /* 280 8 */
struct sk_buff_head {
struct sk_buff * next; /* 288 8 */
struct sk_buff * prev; /* 296 8 */
/* typedef __u32 */ unsigned int qlen; /* 304 4 */
/* typedef spinlock_t */ struct {
/* typedef raw_spinlock_t */ struct {
unsigned int slock; /* 308 4 */
} raw_lock; /* 308 4 */
} lock; /* 308 4 */
} sk_error_queue; /* 288 24 */
struct proto * sk_prot_creator; /* 312 8 */
/* --- cacheline 5 boundary (320 bytes) --- */
/* typedef rwlock_t */ struct {
/* typedef raw_rwlock_t */ struct {
unsigned int lock; /* 320 4 */
} raw_lock; /* 320 4 */
} sk_callback_lock; /* 320 4 */
int sk_err; /* 324 4 */
int sk_err_soft; /* 328 4 */
short unsigned int sk_ack_backlog; /* 332 2 */
short unsigned int sk_max_ack_backlog; /* 334 2 */
/* typedef __u32 */ unsigned int sk_priority; /* 336 4 */
struct ucred {
/* typedef __u32 */ unsigned int pid; /* 340 4 */
/* typedef __u32 */ unsigned int uid; /* 344 4 */
/* typedef __u32 */ unsigned int gid; /* 348 4 */
} sk_peercred; /* 340 12 */
long int sk_rcvtimeo; /* 352 8 */
long int sk_sndtimeo; /* 360 8 */
struct sk_filter * sk_filter; /* 368 8 */
void * sk_protinfo; /* 376 8 */
/* --- cacheline 6 boundary (384 bytes) --- */
struct timer_list {
struct list_head {
struct list_head * next; /* 384 8 */
struct list_head * prev; /* 392 8 */
} entry; /* 384 16 */
long unsigned int expires; /* 400 8 */
void (*function)(long unsigned int); /* 408 8 */
long unsigned int data; /* 416 8 */
struct tvec_t_base_s * base; /* 424 8 */
} sk_timer; /* 384 48 */
/* typedef ktime_t */ union {
/* typedef s64 */ long long int tv64; /* 8 */
} sk_stamp; /* 432 8 */
struct socket * sk_socket; /* 440 8 */
/* --- cacheline 7 boundary (448 bytes) --- */
void * sk_user_data; /* 448 8 */
struct page * sk_sndmsg_page; /* 456 8 */
struct sk_buff * sk_send_head; /* 464 8 */
/* typedef __u32 */ unsigned int sk_sndmsg_off; /* 472 4 */
int sk_write_pending; /* 476 4 */
void * sk_security; /* 480 8 */
void (*sk_state_change)(struct sock *); /* 488 8 */
void (*sk_data_ready)(struct sock *, int); /* 496 8 */
void (*sk_write_space)(struct sock *); /* 504 8 */
/* --- cacheline 8 boundary (512 bytes) --- */
void (*sk_error_report)(struct sock *); /* 512 8 */
int (*sk_backlog_rcv)(struct sock *,
struct sk_buff *); /* 520 8 */
void (*sk_destruct)(struct sock *); /* 528 8 */
}; /* size: 536, cachelines: 9 */ /* sum members: 532, holes: 1, sum holes: 4 */
/* last cacheline: 24 bytes */
- Arnaldo
Quoted 22 lines · On Fri, 4 May 2007 08:24:34 -0300 "Arnaldo Carvalho de Melo"…
Quoted 2 lines · Eric Dumazet
Hum, maybe not enough tea this morning, but I always thought
spinlock_tsize was 4 bytes....Well, unfolding
sk_buff_headto see the details:[acme@mica linux-2.6]$ pahole --expand_types -C
sk_buff_headnet/core/sock.o/* <9a61>/home/acme/git/linux-2.6/include/linux/skbuff.h:111*/struct sk_buff_head { struct sk_buff * next; /* 0 8 */ struct sk_buff * prev; /* 8 8 */ /* typedef __u32 */ unsigned int qlen; /* 16 4 */ /* typedef spinlock_t */ struct { /* typedef raw_spinlock_t */ struct { unsigned int slock; /* 20 4 */ } raw_lock; /* 20 4 */ unsigned int break_lock; /* 24 4 */ } lock; /* 20 8 */ }; /* size: 32, cachelines: 1 *//* padding: 4 */
/* last cacheline: 32 bytes */
Quoted 6 lines
#if defined(CONFIG_PREEMPT) && defined(CONFIG_SMP) unsigned int break_lock;#endif
Disabling
CONFIG_PREEMPTmakes you regain your confidence in the quality of your tea:
PREEMPT ? what is this ? Is it suitable for my machines with hundred of thousands of sockets ? :)
I hope you dont want to shrink struct sock for desktop users :)
BTW, your pahole tool is cool :)