UDP memory usage accounting (take 3)
Subsystems (5)
cover UDP memory usage accounting (take 3)
This patch set try to introduce memory usage accounting for UDP(currently ipv4 only).
This is the second post of take 2 patch, because previous post was broken by my MUA setting.
Only what I chage is my MUA setting. There is no code change from take 2.
This patch set is for 2.6.23-rc8.
I appreciate your comment/test/feedback.
Satoshi Oshima
PATCH 1/3 UDP memory usage accounting (take 3): fix send buffer check
This patch introduces sndbuf size check before
memory allcation for send buffer.
net/ipv4/ip_output.c +5 -0
@@ -1004,6 +1004,11 @@ alloc_new_skb: frag = &skb_shinfo (skb)->frags[i]; } }else if (i <MAX_SKB_FRAGS ) { +if (atomic_read (&sk->sk_wmem_alloc) +PAGE_SIZE + >2 * sk->sk_sndbuf) { + err = -ENOBUFS ; + goto error; + }if (copy >PAGE_SIZE ) copy =PAGE_SIZE ; page =alloc_pages (sk->sk_allocation,0 );
PATCH 2/3 UDP memory usage accounting (take 3): accounting unit and variable
In reply to Satoshi OSHIMAThis patch introduces global variable for UDP memory accounting. The unit is page.
include/net/sock.h +7 -0
@@ -723,6 +723,13 @@ static inline int sk_stream_wmem_schedulsk_stream_mem_schedule (sk, size,0 ); } +#define SK_DATAGRAM_MEM_QUANTUM ((int)PAGE_SIZE) + +static inline int sk_datagram_pages (int amt) +{ +return DIV_ROUND_UP (amt,SK_DATAGRAM_MEM_QUANTUM ); +} +/* Used by processes to "lock" a socket state, so that * interrupts and bottom half handlers won't change it * from under us. It essentially blocks any incoming
include/net/udp.h +2 -0
@@ -65,6 +65,8 @@ extern rwlock_t udp_hash_lock;extern struct proto udp_prot; +extern atomic_t udp_memory_allocated; +struct sk_buff ;/*
net/ipv4/proc.c +2 -1
@@ -66,7 +66,8 @@ static int sockstat_seq_show(struct seq_fold_prot_inuse (&tcp_prot),atomic_read (&tcp_orphan_count), tcp_death_row.tw_count,atomic_read (&tcp_sockets_allocated), atomic_read(&tcp_memory_allocated)); -seq_printf (seq,"UDP: inuse %d\n" ,fold_prot_inuse (&udp_prot)); +seq_printf (seq,"UDP: inuse %d mem %d\n" ,fold_prot_inuse (&udp_prot), +atomic_read (&udp_memory_allocated));seq_printf (seq,"UDPLITE: inuse %d\n" ,fold_prot_inuse (&udplite_prot));seq_printf (seq,"RAW: inuse %d\n" ,fold_prot_inuse (&raw_prot)); seq_printf(seq, "FRAG: inuse %d memory %d\n", ip_frag_nqueues,
net/ipv4/udp.c +4 -0
@@ -113,6 +113,10 @@ DEFINE_SNMP_STAT(struct udp_mib, udp_stastruct hlist_head udp_hash[UDP_HTABLE_SIZE ];DEFINE_RWLOCK (udp_hash_lock); +atomic_t udp_memory_allocated; + +EXPORT_SYMBOL (udp_memory_allocated); +static int udp_port_rover;static inline int __udp_lib_lport_inuse (__u16 num,struct hlist_head udptable[])
PATCH 3/3 UDP memory usage accounting (take 3): measurement
In reply to Satoshi OSHIMAThis patch introduces memory usage measurement for UDP.
net/ipv4/ip_output.c +24 -3
@@ -743,6 +743,8 @@ static inline int ip_ufo_append_data(str/* specify the length of each IP datagram fragment*/ skb_shinfo (skb)->gso_size = mtu - fragheaderlen;skb_shinfo (skb)->gso_type =SKB_GSO_UDP ; +atomic_add (sk_datagram_pages (skb->truesize), + sk->sk_prot->memory_allocated);__skb_queue_tail (&sk->sk_write_queue, skb);return 0 ;
@@ -924,6 +926,9 @@ alloc_new_skb: }if (skb ==NULL ) goto error; +if (sk->sk_prot->memory_allocated) +atomic_add (sk_datagram_pages (skb->truesize), + sk->sk_prot->memory_allocated);/* * Fill in the control structures
@@ -1023,6 +1028,8 @@ alloc_new_skb: frag = &skb_shinfo (skb)->frags[i]; skb->truesize +=PAGE_SIZE ;atomic_add (PAGE_SIZE , &sk->sk_wmem_alloc); +if (sk->sk_prot->memory_allocated) +atomic_inc (sk->sk_prot->memory_allocated); }else { err = -EMSGSIZE ; goto error;
@@ -1123,7 +1130,9 @@ ssize_t ip_append_page(struct sock *sk,if (unlikely (!skb)) { err = -ENOBUFS ; goto error; - } + }else if (sk->sk_prot->memory_allocated) +atomic_add (sk_datagram_pages (skb->truesize), + sk->sk_prot->memory_allocated);/* * Fill in the control structures
@@ -1152,6 +1161,8 @@ ssize_t ip_append_page(struct sock *sk,/* * Put the packet on the pending queue. */ +atomic_add (sk_datagram_pages (skb->truesize), + sk->sk_prot->memory_allocated);__skb_queue_tail (&sk->sk_write_queue, skb);continue ; }
@@ -1202,13 +1213,14 @@ int ip_push_pending_frames(struct sock *struct iphdr *iph;__be16 df =0 ;__u8 ttl; -int err =0 ; +int err =0 , send_page_size;if ((skb =__skb_dequeue (&sk->sk_write_queue)) ==NULL ) goto out; tail_skb = &(skb_shinfo (skb)->frag_list);/* move skb->data to ip header from ext header */ + send_page_size =sk_datagram_pages (skb->truesize);if (skb->data <skb_network_header (skb))__skb_pull (skb,skb_network_offset (skb));while ((tmp_skb =__skb_dequeue (&sk->sk_write_queue)) !=NULL ) {
@@ -1218,6 +1230,7 @@ int ip_push_pending_frames(struct sock * skb->len += tmp_skb->len; skb->data_len += tmp_skb->len; skb->truesize += tmp_skb->truesize; + send_page_size +=sk_datagram_pages (tmp_skb->truesize);__sock_put (tmp_skb->sk); tmp_skb->destructor =NULL ; tmp_skb->sk =NULL ;
@@ -1269,6 +1282,8 @@ int ip_push_pending_frames(struct sock */* Netfilter gets whole the not fragmented skb. */ err =NF_HOOK (PF_INET ,NF_IP_LOCAL_OUT , skb,NULL , skb->dst->dev, dst_output); +if (sk->sk_prot->memory_allocated) +atomic_sub (send_page_size, sk->sk_prot->memory_allocated);if (err) {if (err >0 ) err = inet->recverr ?net_xmit_errno (err) :0 ;
@@ -1298,9 +1313,15 @@ void ip_flush_pending_frames(struct sock {struct inet_sock *inet =inet_sk (sk);struct sk_buff *skb; +int num_flush_mem =0 ; -while ((skb =__skb_dequeue_tail (&sk->sk_write_queue)) !=NULL ) +while ((skb =__skb_dequeue_tail (&sk->sk_write_queue)) !=NULL ) { + num_flush_mem +=sk_datagram_pages (skb->truesize);kfree_skb (skb); + } + +if (sk->sk_prot->memory_allocated) +atomic_sub (num_flush_mem, sk->sk_prot->memory_allocated); inet->cork.flags &= ~IPCORK_OPT ;kfree (inet->cork.opt);
net/ipv4/udp.c +10 -0
@@ -887,6 +887,9 @@ try_again: err = ulen; out_free: +atomic_sub (sk_datagram_pages (skb->truesize), + sk->sk_prot->memory_allocated); +skb_free_datagram (sk, skb); out:return err;
@@ -894,6 +897,9 @@ out: csum_copy_err:UDP_INC_STATS_BH (UDP_MIB_INERRORS , is_udplite); +atomic_sub (sk_datagram_pages (skb->truesize), + sk->sk_prot->memory_allocated); +skb_kill_datagram (sk, skb, flags);if (noblock)
@@ -1019,6 +1025,9 @@ int udp_queue_rcv_skb(struct sock * sk, goto drop; } +atomic_add (sk_datagram_pages (skb->truesize), + sk->sk_prot->memory_allocated); +UDP_INC_STATS_BH (UDP_MIB_INDATAGRAMS , up->pcflag);return 0 ;
@@ -1443,6 +1452,7 @@ struct proto udp_prot = { .hash = udp_lib_hash, .unhash = udp_lib_unhash, .get_port = udp_v4_get_port, + .memory_allocated = &udp_memory_allocated, .obj_size =sizeof (struct udp_sock ), #ifdef CONFIG_COMPAT .compat_setsockopt = compat_udp_setsockopt,
net/ipv4/af_inet.c +29 -1
@@ -126,13 +126,41 @@ extern void ip_mc_drop_socket(struct socstatic struct list_head inetsw[SOCK_MAX ];static DEFINE_SPINLOCK (inetsw_lock); +/** +* __skb_queue_purge_and_sub_memory_allocated +* - empty a list and subtruct memory allocation counter +* @sk: sk +* @list: list to empty +* Delete all buffers on an &sk_buff list and subtruct the +* turesize of the sk_buff for memory accounting. Each buffer +* is removed from the list and one reference dropped. This +* function does not take the list lock and the caller must +* hold the relevant locks to use it. +*/ +void __skb_queue_purge_and_sub_memory_allocated (struct sock *sk, +struct sk_buff_head *list) +{ +struct sk_buff *skb; +int purged_skb_size =0 ; +while ((skb =__skb_dequeue (list)) !=NULL ) { + purged_skb_size +=sk_datagram_pages (skb->truesize); +kfree_skb (skb); + } +atomic_sub (purged_skb_size, sk->sk_prot->memory_allocated); +} +/* New destruction routine */ void inet_sock_destruct (struct sock *sk) {struct inet_sock *inet =inet_sk (sk); -__skb_queue_purge (&sk->sk_receive_queue); +if (sk->sk_prot->memory_allocated && sk->sk_type !=SOCK_STREAM ) +__skb_queue_purge_and_sub_memory_allocated (sk, + &sk->sk_receive_queue); +else +__skb_queue_purge (&sk->sk_receive_queue); +__skb_queue_purge (&sk->sk_error_queue);if (sk->sk_type ==SOCK_STREAM && sk->sk_state !=TCP_CLOSE ) {