diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 838eea92f8f2d74c750fc968750b348d769f1f05..5bc9fe749d83388f88c6251d62c9f497aa4ab467 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -826,7 +826,7 @@ static int cp_start_xmit (struct sk_buff *skb, struct net_device *dev) * Otherwise we could race with the device. */ first_eor = eor; - first_len = skb->len - skb->data_len; + first_len = skb_headlen(skb); first_mapping = pci_map_single(cp->pdev, skb->data, first_len, PCI_DMA_TODEVICE); cp->tx_skb[entry].skb = skb; diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c index e05a28c98a944b9cbbfb6c3ecf9c85d82045c4ef..502e1f0414bab25b53e42ef379545947d5fbdf71 100644 --- a/drivers/net/acenic.c +++ b/drivers/net/acenic.c @@ -2829,7 +2829,7 @@ static int ace_start_xmit(struct sk_buff *skb, struct net_device *dev) int i, len = 0; mapping = ace_map_tx_skb(ap, skb, NULL, idx); - flagsize = ((skb->len - skb->data_len) << 16); + flagsize = (skb_headlen(skb) << 16); if (skb->ip_summed == CHECKSUM_HW) flagsize |= BD_FLG_TCP_UDP_SUM; #if ACENIC_DO_VLAN diff --git a/drivers/net/e100/e100_main.c b/drivers/net/e100/e100_main.c index d241eaa64e2e8a2f7f3f075a3b0a2bdd2be5708b..2f3f6f6b1e8da3045debbeed18bb8cbb1614a6ab 100644 --- a/drivers/net/e100/e100_main.c +++ b/drivers/net/e100/e100_main.c @@ -2199,10 +2199,10 @@ e100_prepare_xmit_buff(struct e100_private *bdp, struct sk_buff *skb) (tcb->tbd_ptr)->tbd_buf_addr = cpu_to_le32(pci_map_single(bdp->pdev, skb->data, - (skb->len - skb->data_len), + skb_headlen(skb), PCI_DMA_TODEVICE)); (tcb->tbd_ptr)->tbd_buf_cnt = - cpu_to_le16(skb->len - skb->data_len); + cpu_to_le16(skb_headlen(skb)); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++, tbd_arr_ptr++, frag++) { diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 7c139fdafea6c3382a2031d2732ba73bc3941cf7..daeb1f39e9c81c315fafe0c9c6bba2c11b852644 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -894,7 +894,7 @@ static int gem_start_xmit(struct sk_buff *skb, struct net_device *dev) /* We must give this initial chunk to the device last. * Otherwise we could race with the device. */ - first_len = skb->len - skb->data_len; + first_len = skb_headlen(skb); first_mapping = pci_map_page(gp->pdev, virt_to_page(skb->data), ((unsigned long) skb->data & ~PAGE_MASK), first_len, PCI_DMA_TODEVICE); diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index 5b34d6ecb03bdfcf6b52d0fa057d40f1b25ad98a..631d07021d815dc0abe9920ea609e2deab3abedf 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -2319,7 +2319,7 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct net_device *dev) /* We must give this initial chunk to the device last. * Otherwise we could race with the device. */ - first_len = skb->len - skb->data_len; + first_len = skb_headlen(skb); first_mapping = hme_dma_map(hp, skb->data, first_len, DMA_TODEVICE); entry = NEXT_TX(entry); diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index ea4082646fdee480dc97f46f89a1f816386a9b5f..07a311b66d3c5000f8b3a8ee445ceada29cebdb5 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -1751,7 +1751,7 @@ static void tg3_tx(struct tg3 *tp) pci_unmap_single(tp->pdev, pci_unmap_addr(ri, mapping), - (skb->len - skb->data_len), + skb_headlen(skb), PCI_DMA_TODEVICE); ri->skb = NULL; @@ -2316,7 +2316,7 @@ static int tigon3_4gb_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb, int len; if (i == 0) - len = skb->len - skb->data_len; + len = skb_headlen(skb); else len = skb_shinfo(skb)->frags[i-1].size; pci_unmap_single(tp->pdev, @@ -2401,7 +2401,7 @@ static int tg3_start_xmit_4gbug(struct sk_buff *skb, struct net_device *dev) int would_hit_hwbug; unsigned long flags; - len = (skb->len - skb->data_len); + len = skb_headlen(skb); /* No BH disabling for tx_lock here. We are running in BH disabled * context and TX reclaim runs via tp->poll inside of a software @@ -2520,7 +2520,7 @@ static int tg3_start_xmit_4gbug(struct sk_buff *skb, struct net_device *dev) i = 0; while (entry != last_plus_one) { if (i == 0) - len = skb->len - skb->data_len; + len = skb_headlen(skb); else len = skb_shinfo(skb)->frags[i-1].size; @@ -2593,7 +2593,7 @@ static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) u32 len, entry, base_flags, mss; unsigned long flags; - len = (skb->len - skb->data_len); + len = skb_headlen(skb); /* No BH disabling for tx_lock here. We are running in BH disabled * context and TX reclaim runs via tp->poll inside of a software @@ -2829,7 +2829,7 @@ static void tg3_free_rings(struct tg3 *tp) pci_unmap_single(tp->pdev, pci_unmap_addr(txp, mapping), - (skb->len - skb->data_len), + skb_headlen(skb), PCI_DMA_TODEVICE); txp->skb = NULL; diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index e6c46f2f41a0ddcc8cc458627c23f5810079545d..6a6a8eb72c10e5141ca97edab6a724c0e7662a67 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -844,7 +844,7 @@ typhoon_start_tx(struct sk_buff *skb, struct net_device *dev) } else { int i, len; - len = skb->len - skb->data_len; + len = skb_headlen(skb); skb_dma = pci_map_single(tp->tx_pdev, skb->data, len, PCI_DMA_TODEVICE); txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID; diff --git a/include/linux/irda.h b/include/linux/irda.h index bdf23baa5483b92277ead5dc8e6f60d7f6b28627..948488ece2a8e9d7bd9f5107249370b3399571c6 100644 --- a/include/linux/irda.h +++ b/include/linux/irda.h @@ -25,6 +25,8 @@ #ifndef KERNEL_IRDA_H #define KERNEL_IRDA_H +#include <linux/socket.h> /* only for sa_family_t */ + /* Hint bit positions for first hint byte */ #define HINT_PNP 0x01 #define HINT_PDA 0x02 diff --git a/include/linux/net.h b/include/linux/net.h index 55083c9382982c58c6d2e59f943818b28874816e..6ba6e10385211963c52b408947dcf828d14598b0 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -19,7 +19,6 @@ #define _LINUX_NET_H #include <linux/config.h> -#include <linux/socket.h> #include <linux/wait.h> struct poll_table_struct; @@ -88,6 +87,8 @@ struct socket { struct vm_area_struct; struct page; struct kiocb; +struct sockaddr; +struct msghdr; struct proto_ops { int family; @@ -136,6 +137,8 @@ struct net_proto_family { short encrypt_net; }; +struct iovec; + extern int sock_wake_async(struct socket *sk, int how, int band); extern int sock_register(struct net_proto_family *fam); extern int sock_unregister(int family); diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 69346c394563ef687481234e3ef3e6140d9dbde4..30de192de7a9587bb54ab52393ea9ebf12f9087b 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -1,6 +1,8 @@ #ifndef __LINUX_NETLINK_H #define __LINUX_NETLINK_H +#include <linux/socket.h> /* for sa_family_t */ + #define NETLINK_ROUTE 0 /* Routing/device hook */ #define NETLINK_SKIP 1 /* Reserved for ENskip */ #define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ diff --git a/include/net/tcp.h b/include/net/tcp.h index b652650286fee9a8555766ab6df167dcb25e8395..8844d3f3d8f95c36ff6b7e125838a2b785de8927 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -31,6 +31,7 @@ #include <linux/percpu.h> #include <net/checksum.h> #include <net/sock.h> +#include <net/snmp.h> #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) #include <linux/ipv6.h> #endif @@ -639,6 +640,8 @@ DECLARE_SNMP_STAT(struct tcp_mib, tcp_statistics); #define TCP_INC_STATS_BH(field) SNMP_INC_STATS_BH(tcp_statistics, field) #define TCP_INC_STATS_USER(field) SNMP_INC_STATS_USER(tcp_statistics, field) #define TCP_DEC_STATS(field) SNMP_DEC_STATS(tcp_statistics, field) +#define TCP_ADD_STATS_BH(field, val) SNMP_ADD_STATS_BH(tcp_statistics, field, val) +#define TCP_ADD_STATS_USER(field, val) SNMP_ADD_STATS_USER(tcp_statistics, field, val) extern __inline__ void tcp_put_port(struct sock *sk); extern void tcp_inherit_port(struct sock *sk, struct sock *child); @@ -1398,6 +1401,9 @@ static __inline__ void tcp_set_state(struct sock *sk, int state) break; case TCP_CLOSE: + if (oldstate == TCP_CLOSE_WAIT || oldstate == TCP_ESTABLISHED) + TCP_INC_STATS(TcpEstabResets); + sk->prot->unhash(sk); if (sk->prev && !(sk->userlocks&SOCK_BINDPORT_LOCK)) tcp_put_port(sk); @@ -1878,4 +1884,13 @@ static inline int tcp_use_frto(const struct sock *sk) tp->snd_una + tp->snd_wnd)); } +static inline void tcp_mib_init(void) +{ + /* See RFC 2012 */ + TCP_ADD_STATS_USER(TcpRtoAlgorithm, 1); + TCP_ADD_STATS_USER(TcpRtoMin, TCP_RTO_MIN*1000/HZ); + TCP_ADD_STATS_USER(TcpRtoMax, TCP_RTO_MAX*1000/HZ); + TCP_ADD_STATS_USER(TcpMaxConn, -1); +} + #endif /* _TCP_H */ diff --git a/include/net/xfrm.h b/include/net/xfrm.h index fe32e5c323541c1dc810861a5d59b0daa84c5125..b75d2651adf06323a74eb910d25bb05558acdbad 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -84,6 +84,7 @@ extern struct semaphore xfrm_cfg_sem; /* Full description of state of transformer. */ struct xfrm_state { + /* Note: bydst is re-used during gc */ struct list_head bydst; struct list_head byspi; diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 25562366d4c0700c3faac01abc427b18bbf1fb6b..fc59bfc7a8bb9abeb205f6091eb176bf9e6cde76 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -29,7 +29,6 @@ #include <net/p8022.h> #include <net/arp.h> #include <linux/rtnetlink.h> -#include <linux/brlock.h> #include <linux/notifier.h> #include <linux/if_vlan.h> @@ -68,7 +67,6 @@ static struct packet_type vlan_packet_type = { .dev =NULL, .func = vlan_skb_recv, /* VLAN receive method */ .data = (void *)(-1), /* Set here '(void *)1' when this code can SHARE SKBs */ - .next = NULL }; /* End of global variables definitions. */ @@ -231,9 +229,8 @@ static int unregister_vlan_dev(struct net_device *real_dev, real_dev->vlan_rx_kill_vid(real_dev, vlan_id); } - br_write_lock(BR_NETPROTO_LOCK); grp->vlan_devices[vlan_id] = NULL; - br_write_unlock(BR_NETPROTO_LOCK); + synchronize_net(); /* Caller unregisters (and if necessary, puts) @@ -266,7 +263,7 @@ static int unregister_vlan_dev(struct net_device *real_dev, ret = 1; } - MOD_DEC_USE_COUNT; + module_put(THIS_MODULE); } } @@ -433,6 +430,7 @@ static struct net_device *register_vlan_device(const char *eth_IF_name, /* set up method calls */ new_dev->init = vlan_dev_init; new_dev->destructor = vlan_dev_destruct; + new_dev->owner = THIS_MODULE; /* new_dev->ifindex = 0; it will be set when added to * the global list. @@ -540,16 +538,22 @@ static struct net_device *register_vlan_device(const char *eth_IF_name, register_netdevice(new_dev); rtnl_unlock(); - + /* NOTE: We have a reference to the real device, * so hold on to the reference. */ - MOD_INC_USE_COUNT; /* Add was a success!! */ + if (!try_module_get(THIS_MODULE)) + goto out_module_dying; + #ifdef VLAN_DEBUG printk(VLAN_DBG "Allocated new device successfully, returning.\n"); #endif return new_dev; +out_module_dying: + rtnl_lock(); + unregister_netdevice(new_dev); + out_free_newdev_priv: kfree(new_dev->priv); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 907073996bc77d3e88f8b44b82a2925b0ab4fc69..24a4462cc152f1aec78492923b7c5f3656773871 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -31,7 +31,6 @@ #include <net/datalink.h> #include <net/p8022.h> #include <net/arp.h> -#include <linux/brlock.h> #include "vlan.h" #include "vlanproc.h" diff --git a/net/core/datagram.c b/net/core/datagram.c index f83189e52b130399eaf8980b26817a7742549c20..7474e087fa122c5c49bdedfc6997d796ae6ea279 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -220,7 +220,7 @@ int skb_copy_datagram(const struct sk_buff *skb, int offset, char *to, int size) int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, struct iovec *to, int len) { - int start = skb->len - skb->data_len; + int start = skb_headlen(skb); int i, copy = start - offset; /* Copy header. */ @@ -295,7 +295,7 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset, int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int *csump) { - int start = skb->len - skb->data_len; + int start = skb_headlen(skb); int pos = 0; int i, copy = start - offset; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index c909a7d3f00a3f5931b3cc30f3ffa2b49e2c0eab..bb68b12b00471d230088995afd5a4108836662d2 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -932,7 +932,7 @@ unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta) int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len) { int i, copy; - int start = skb->len - skb->data_len; + int start = skb_headlen(skb); if (offset > (int)skb->len - len) goto fault; @@ -1009,7 +1009,7 @@ int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len) unsigned int skb_checksum(const struct sk_buff *skb, int offset, int len, unsigned int csum) { - int start = skb->len - skb->data_len; + int start = skb_headlen(skb); int i, copy = start - offset; int pos = 0; @@ -1085,7 +1085,7 @@ unsigned int skb_checksum(const struct sk_buff *skb, int offset, unsigned int skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int csum) { - int start = skb->len - skb->data_len; + int start = skb_headlen(skb); int i, copy = start - offset; int pos = 0; @@ -1170,9 +1170,9 @@ void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to) if (skb->ip_summed == CHECKSUM_HW) csstart = skb->h.raw - skb->data; else - csstart = skb->len - skb->data_len; + csstart = skb_headlen(skb); - if (csstart > skb->len - skb->data_len) + if (csstart > skb_headlen(skb)) BUG(); memcpy(to, skb->data, csstart); diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 2082abcdc2d3f869b3a71cf7a4c3ca3f9597ed60..390c24c2a45be009bc1e1133a07a480433fcd90d 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1108,6 +1108,8 @@ static int __init init_ipv4_mibs(void) } } + (void) tcp_mib_init(); + return 0; } diff --git a/net/ipv4/ah.c b/net/ipv4/ah.c index 1f4830a9431900b1ef623d902e936e6051a53f1f..02f08aa24437662fbd3bce87324c35ec1d4f9af7 100644 --- a/net/ipv4/ah.c +++ b/net/ipv4/ah.c @@ -314,12 +314,14 @@ static void ah_destroy(struct xfrm_state *x) crypto_free_tfm(ahp->tfm); ahp->tfm = NULL; } + kfree(ahp); } static struct xfrm_type ah_type = { .description = "AH4", + .owner = THIS_MODULE, .proto = IPPROTO_AH, .init_state = ah_init_state, .destructor = ah_destroy, @@ -335,7 +337,6 @@ static struct inet_protocol ah4_protocol = { static int __init ah4_init(void) { - SET_MODULE_OWNER(&ah_type); if (xfrm_register_type(&ah_type, AF_INET) < 0) { printk(KERN_INFO "ip ah init: can't add xfrm type\n"); return -EAGAIN; diff --git a/net/ipv4/esp.c b/net/ipv4/esp.c index 2fe10539c3713970216ea730f7321f363658681b..db29b32b118e6a869ae2846bbc9d75eaec49b2b5 100644 --- a/net/ipv4/esp.c +++ b/net/ipv4/esp.c @@ -452,6 +452,7 @@ void esp_destroy(struct xfrm_state *x) kfree(esp->auth.work_icv); esp->auth.work_icv = NULL; } + kfree(esp); } int esp_init_state(struct xfrm_state *x, void *args) @@ -552,6 +553,7 @@ int esp_init_state(struct xfrm_state *x, void *args) static struct xfrm_type esp_type = { .description = "ESP4", + .owner = THIS_MODULE, .proto = IPPROTO_ESP, .init_state = esp_init_state, .destructor = esp_destroy, diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index c78c0ac544b62543a883f4450fef4118076bda9d..d07556385a0b390d743c0d17156739553214a61a 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -143,9 +143,15 @@ static int snmp_seq_show(struct seq_file *seq, void *v) "InSegs OutSegs RetransSegs InErrs OutRsts\nTcp:"); for (i = 0; - i < offsetof(struct tcp_mib, __pad) / sizeof(unsigned long); i++) - seq_printf(seq, " %lu", - fold_field((void **) tcp_statistics, i)); + i < offsetof(struct tcp_mib, __pad) / sizeof(unsigned long); i++) { + if (i == (offsetof(struct tcp_mib, TcpMaxConn) / sizeof(unsigned long))) + /* MaxConn field is negative, RFC 2012 */ + seq_printf(seq, " %ld", + fold_field((void **) tcp_statistics, i)); + else + seq_printf(seq, " %lu", + fold_field((void **) tcp_statistics, i)); + } seq_printf(seq, "\nUdp: InDatagrams NoPorts InErrors OutDatagrams\n" "Udp:"); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index fd3baeb58b8fdbaaa25c0aac70b4ad17599e9757..344353ac7a6fbaa9770c000bed0a599821902393 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -354,7 +354,7 @@ void tcp_push_one(struct sock *sk, unsigned cur_mss) static void skb_split(struct sk_buff *skb, struct sk_buff *skb1, u32 len) { int i; - int pos = skb->len - skb->data_len; + int pos = skb_headlen(skb); if (len < pos) { /* Split line is inside header. */ diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 0130c3b7889b297e581da9e2facabb5ed8057c2b..746dc9697b15ac58d5691c1a94b278eead65c619 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -313,11 +313,13 @@ static void ah6_destroy(struct xfrm_state *x) crypto_free_tfm(ahp->tfm); ahp->tfm = NULL; } + kfree(ahp); } static struct xfrm_type ah6_type = { .description = "AH6", + .owner = THIS_MODULE, .proto = IPPROTO_AH, .init_state = ah6_init_state, .destructor = ah6_destroy, @@ -333,8 +335,6 @@ static struct inet6_protocol ah6_protocol = { int __init ah6_init(void) { - SET_MODULE_OWNER(&ah6_type); - if (xfrm_register_type(&ah6_type, AF_INET6) < 0) { printk(KERN_INFO "ipv6 ah init: can't add xfrm type\n"); return -EAGAIN; diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 5f9275aabf0f57c2172b58aa0396e2c27c6cadcd..764a5d8ac1ea7290a93c06e1b629b8a7ad65c836 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -406,6 +406,7 @@ void esp6_destroy(struct xfrm_state *x) kfree(esp->auth.work_icv); esp->auth.work_icv = NULL; } + kfree(esp); } int esp6_init_state(struct xfrm_state *x, void *args) @@ -488,6 +489,7 @@ int esp6_init_state(struct xfrm_state *x, void *args) static struct xfrm_type esp6_type = { .description = "ESP6", + .owner = THIS_MODULE, .proto = IPPROTO_ESP, .init_state = esp6_init_state, .destructor = esp6_destroy, @@ -504,7 +506,6 @@ static struct inet6_protocol esp6_protocol = { int __init esp6_init(void) { - SET_MODULE_OWNER(&esp6_type); if (xfrm_register_type(&esp6_type, AF_INET6) < 0) { printk(KERN_INFO "ipv6 esp init: can't add xfrm type\n"); return -EAGAIN; diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c index d01a6fd0245612979d4eba1df006d240eb897d8c..b850f0285cf2e73308770d0a749096d327c29ebd 100644 --- a/net/xfrm/xfrm_algo.c +++ b/net/xfrm/xfrm_algo.c @@ -445,7 +445,7 @@ int xfrm_count_enc_supported(void) void skb_icv_walk(const struct sk_buff *skb, struct crypto_tfm *tfm, int offset, int len, icv_update_fn_t icv_update) { - int start = skb->len - skb->data_len; + int start = skb_headlen(skb); int i, copy = start - offset; struct scatterlist sg; @@ -521,7 +521,7 @@ void skb_icv_walk(const struct sk_buff *skb, struct crypto_tfm *tfm, int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len) { - int start = skb->len - skb->data_len; + int start = skb_headlen(skb); int i, copy = start - offset; int elt = 0; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index b0460abbde2d085947e5fda34196d9aed9083302..321858bf3af23f57ff52a8c8e33f17c0ed2a14e5 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -963,6 +963,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, * are implied between each two transformations. */ for (i = pol->xfrm_nr-1, k = 0; i >= 0; i--) { + if (pol->xfrm_vec[i].optional) + continue; k = xfrm_policy_ok(pol->xfrm_vec+i, sp, k, family); if (k < 0) goto reject; diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 162fa975f05062a5ac797255c1b77022e7dc5a87..8f435d8e51faa95e538197719bb941f24553f4ba 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -13,6 +13,7 @@ * */ +#include <linux/workqueue.h> #include <net/xfrm.h> #include <linux/pfkeyv2.h> #include <linux/ipsec.h> @@ -41,8 +42,48 @@ DECLARE_WAIT_QUEUE_HEAD(km_waitq); static rwlock_t xfrm_state_afinfo_lock = RW_LOCK_UNLOCKED; static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO]; +static struct work_struct xfrm_state_gc_work; +static struct list_head xfrm_state_gc_list = LIST_HEAD_INIT(xfrm_state_gc_list); +static spinlock_t xfrm_state_gc_lock = SPIN_LOCK_UNLOCKED; + static void __xfrm_state_delete(struct xfrm_state *x); +static void xfrm_state_gc_destroy(struct xfrm_state *x) +{ + if (del_timer(&x->timer)) + BUG(); + if (x->aalg) + kfree(x->aalg); + if (x->ealg) + kfree(x->ealg); + if (x->calg) + kfree(x->calg); + if (x->encap) + kfree(x->encap); + if (x->type) { + x->type->destructor(x); + xfrm_put_type(x->type); + } + kfree(x); + wake_up(&km_waitq); +} + +static void xfrm_state_gc_task(void *data) +{ + struct xfrm_state *x; + struct list_head *entry, *tmp; + struct list_head gc_list = LIST_HEAD_INIT(gc_list); + + spin_lock_bh(&xfrm_state_gc_lock); + list_splice_init(&xfrm_state_gc_list, &gc_list); + spin_unlock_bh(&xfrm_state_gc_lock); + + list_for_each_safe(entry, tmp, &gc_list) { + x = list_entry(entry, struct xfrm_state, bydst); + xfrm_state_gc_destroy(x); + } +} + static inline unsigned long make_jiffies(long secs) { if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ) @@ -149,28 +190,17 @@ struct xfrm_state *xfrm_state_alloc(void) void __xfrm_state_destroy(struct xfrm_state *x) { BUG_TRAP(x->km.state == XFRM_STATE_DEAD); - if (del_timer(&x->timer)) - BUG(); - if (x->aalg) - kfree(x->aalg); - if (x->ealg) - kfree(x->ealg); - if (x->calg) - kfree(x->calg); - if (x->encap) - kfree(x->encap); - if (x->type) - xfrm_put_type(x->type); - kfree(x); + + spin_lock_bh(&xfrm_state_gc_lock); + list_add(&x->bydst, &xfrm_state_gc_list); + spin_unlock_bh(&xfrm_state_gc_lock); + schedule_work(&xfrm_state_gc_work); } static void __xfrm_state_delete(struct xfrm_state *x) { - int kill = 0; - if (x->km.state != XFRM_STATE_DEAD) { x->km.state = XFRM_STATE_DEAD; - kill = 1; spin_lock(&xfrm_state_lock); list_del(&x->bydst); atomic_dec(&x->refcnt); @@ -189,22 +219,17 @@ static void __xfrm_state_delete(struct xfrm_state *x) */ if (atomic_read(&x->refcnt) > 2) xfrm_flush_bundles(x); - } - /* All xfrm_state objects are created by one of two possible - * paths: - * - * 1) xfrm_state_alloc --> xfrm_state_insert - * 2) xfrm_state_lookup --> xfrm_state_insert - * - * The xfrm_state_lookup or xfrm_state_alloc call gives a - * reference, and that is what we are dropping here. - */ - atomic_dec(&x->refcnt); - - if (kill && x->type) - x->type->destructor(x); - wake_up(&km_waitq); + /* All xfrm_state objects are created by one of two possible + * paths: + * + * 2) xfrm_state_lookup --> xfrm_state_insert + * + * The xfrm_state_lookup or xfrm_state_alloc call gives a + * reference, and that is what we are dropping here. + */ + atomic_dec(&x->refcnt); + } } void xfrm_state_delete(struct xfrm_state *x) @@ -773,5 +798,6 @@ void __init xfrm_state_init(void) INIT_LIST_HEAD(&xfrm_state_bydst[i]); INIT_LIST_HEAD(&xfrm_state_byspi[i]); } + INIT_WORK(&xfrm_state_gc_work, xfrm_state_gc_task, NULL); }