提交 152a6a9d 编写于 作者: L Linus Torvalds

Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6

* master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6: (21 commits)
  [IPV4] SNMP: Support OutMcastPkts and OutBcastPkts
  [IPV4] SNMP: Support InMcastPkts and InBcastPkts
  [IPV4] SNMP: Support InTruncatedPkts
  [IPV4] SNMP: Support InNoRoutes
  [SNMP]: Add definitions for {In,Out}BcastPkts
  [TCP] FRTO: RFC4138 allows Nagle override when new data must be sent
  [TCP] FRTO: Delay skb available check until it's mandatory
  [XFRM]: Restrict upper layer information by bundle.
  [TCP]: Catch skb with S+L bugs earlier
  [PATCH] INET : IPV4 UDP lookups converted to a 2 pass algo
  [L2TP]: Add the ability to autoload a pppox protocol module.
  [SKB]: Introduce skb_queue_walk_safe()
  [AF_IUCV/IUCV]: smp_call_function deadlock
  [IPV6]: Fix slab corruption running ip6sic
  [TCP]: Update references in two old comments
  [XFRM]: Export SPD info
  [IPV6]: Track device renames in snmp6.
  [SCTP]: Fix sctp_getsockopt_local_addrs_old() to use local storage.
  [NET]: Remove NETIF_F_INTERNAL_STATS, default to internal stats.
  [NETPOLL]: Remove CONFIG_NETPOLL_RX
  ...
......@@ -109,9 +109,6 @@ static void appldata_get_net_sum_data(void *data)
read_lock(&dev_base_lock);
for (dev = dev_base; dev != NULL; dev = dev->next) {
stats = dev->get_stats(dev);
if (stats == NULL) {
continue;
}
rx_packets += stats->rx_packets;
tx_packets += stats->tx_packets;
rx_bytes += stats->rx_bytes;
......
......@@ -2927,11 +2927,6 @@ endif #NETDEVICES
config NETPOLL
def_bool NETCONSOLE
config NETPOLL_RX
bool "Netpoll support for trapping incoming packets"
default n
depends on NETPOLL
config NETPOLL_TRAP
bool "Netpoll traffic trapping"
default n
......
......@@ -1360,13 +1360,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
goto err_undo_flags;
}
if (slave_dev->get_stats == NULL) {
printk(KERN_NOTICE DRV_NAME
": %s: the driver for slave device %s does not provide "
"get_stats function, network statistics will be "
"inaccurate.\n", bond_dev->name, slave_dev->name);
}
new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
if (!new_slave) {
res = -ENOMEM;
......@@ -3641,33 +3634,31 @@ static struct net_device_stats *bond_get_stats(struct net_device *bond_dev)
bond_for_each_slave(bond, slave, i) {
sstats = slave->dev->get_stats(slave->dev);
if (sstats) {
stats->rx_packets += sstats->rx_packets;
stats->rx_bytes += sstats->rx_bytes;
stats->rx_errors += sstats->rx_errors;
stats->rx_dropped += sstats->rx_dropped;
stats->tx_packets += sstats->tx_packets;
stats->tx_bytes += sstats->tx_bytes;
stats->tx_errors += sstats->tx_errors;
stats->tx_dropped += sstats->tx_dropped;
stats->multicast += sstats->multicast;
stats->collisions += sstats->collisions;
stats->rx_length_errors += sstats->rx_length_errors;
stats->rx_over_errors += sstats->rx_over_errors;
stats->rx_crc_errors += sstats->rx_crc_errors;
stats->rx_frame_errors += sstats->rx_frame_errors;
stats->rx_fifo_errors += sstats->rx_fifo_errors;
stats->rx_missed_errors += sstats->rx_missed_errors;
stats->tx_aborted_errors += sstats->tx_aborted_errors;
stats->tx_carrier_errors += sstats->tx_carrier_errors;
stats->tx_fifo_errors += sstats->tx_fifo_errors;
stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors;
stats->tx_window_errors += sstats->tx_window_errors;
}
stats->rx_packets += sstats->rx_packets;
stats->rx_bytes += sstats->rx_bytes;
stats->rx_errors += sstats->rx_errors;
stats->rx_dropped += sstats->rx_dropped;
stats->tx_packets += sstats->tx_packets;
stats->tx_bytes += sstats->tx_bytes;
stats->tx_errors += sstats->tx_errors;
stats->tx_dropped += sstats->tx_dropped;
stats->multicast += sstats->multicast;
stats->collisions += sstats->collisions;
stats->rx_length_errors += sstats->rx_length_errors;
stats->rx_over_errors += sstats->rx_over_errors;
stats->rx_crc_errors += sstats->rx_crc_errors;
stats->rx_frame_errors += sstats->rx_frame_errors;
stats->rx_fifo_errors += sstats->rx_fifo_errors;
stats->rx_missed_errors += sstats->rx_missed_errors;
stats->tx_aborted_errors += sstats->tx_aborted_errors;
stats->tx_carrier_errors += sstats->tx_carrier_errors;
stats->tx_fifo_errors += sstats->tx_fifo_errors;
stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors;
stats->tx_window_errors += sstats->tx_window_errors;
}
read_unlock_bh(&bond->lock);
......
......@@ -31,6 +31,7 @@
#include <linux/ppp_defs.h>
#include <linux/if_ppp.h>
#include <linux/ppp_channel.h>
#include <linux/kmod.h>
#include <net/sock.h>
......@@ -114,6 +115,13 @@ static int pppox_create(struct socket *sock, int protocol)
goto out;
rc = -EPROTONOSUPPORT;
#ifdef CONFIG_KMOD
if (!pppox_protos[protocol]) {
char buffer[32];
sprintf(buffer, "pppox-proto-%d", protocol);
request_module(buffer);
}
#endif
if (!pppox_protos[protocol] ||
!try_module_get(pppox_protos[protocol]->owner))
goto out;
......
......@@ -373,8 +373,6 @@ static __inline__ int led_get_net_activity(void)
if (LOOPBACK(in_dev->ifa_list->ifa_local))
continue;
stats = dev->get_stats(dev);
if (!stats)
continue;
rx_total += stats->rx_packets;
tx_total += stats->tx_packets;
}
......
......@@ -325,7 +325,6 @@ struct net_device
#define NETIF_F_VLAN_CHALLENGED 1024 /* Device cannot handle VLAN packets */
#define NETIF_F_GSO 2048 /* Enable software GSO. */
#define NETIF_F_LLTX 4096 /* LockLess TX */
#define NETIF_F_INTERNAL_STATS 8192 /* Use stats structure in net_device */
/* Segmentation offload features */
#define NETIF_F_GSO_SHIFT 16
......@@ -654,8 +653,10 @@ static inline void netif_start_queue(struct net_device *dev)
static inline void netif_wake_queue(struct net_device *dev)
{
#ifdef CONFIG_NETPOLL_TRAP
if (netpoll_trap())
if (netpoll_trap()) {
clear_bit(__LINK_STATE_XOFF, &dev->state);
return;
}
#endif
if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state))
__netif_schedule(dev);
......@@ -663,10 +664,6 @@ static inline void netif_wake_queue(struct net_device *dev)
static inline void netif_stop_queue(struct net_device *dev)
{
#ifdef CONFIG_NETPOLL_TRAP
if (netpoll_trap())
return;
#endif
set_bit(__LINK_STATE_XOFF, &dev->state);
}
......
......@@ -1471,6 +1471,11 @@ static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len)
prefetch(skb->next), (skb != (struct sk_buff *)(queue)); \
skb = skb->next)
#define skb_queue_walk_safe(queue, skb, tmp) \
for (skb = (queue)->next, tmp = skb->next; \
skb != (struct sk_buff *)(queue); \
skb = tmp, tmp = skb->next)
#define skb_queue_reverse_walk(queue, skb) \
for (skb = (queue)->prev; \
prefetch(skb->prev), (skb != (struct sk_buff *)(queue)); \
......
......@@ -40,6 +40,8 @@ enum
IPSTATS_MIB_FRAGCREATES, /* FragCreates */
IPSTATS_MIB_INMCASTPKTS, /* InMcastPkts */
IPSTATS_MIB_OUTMCASTPKTS, /* OutMcastPkts */
IPSTATS_MIB_INBCASTPKTS, /* InBcastPkts */
IPSTATS_MIB_OUTBCASTPKTS, /* OutBcastPkts */
__IPSTATS_MIB_MAX
};
......
......@@ -185,6 +185,11 @@ enum {
#define XFRM_MSG_NEWSADINFO XFRM_MSG_NEWSADINFO
XFRM_MSG_GETSADINFO,
#define XFRM_MSG_GETSADINFO XFRM_MSG_GETSADINFO
XFRM_MSG_NEWSPDINFO,
#define XFRM_MSG_NEWSPDINFO XFRM_MSG_NEWSPDINFO
XFRM_MSG_GETSPDINFO,
#define XFRM_MSG_GETSPDINFO XFRM_MSG_GETSPDINFO
__XFRM_MSG_MAX
};
#define XFRM_MSG_MAX (__XFRM_MSG_MAX - 1)
......@@ -290,6 +295,36 @@ enum xfrm_sadattr_type_t {
#define XFRMA_SAD_MAX (__XFRMA_SAD_MAX - 1)
};
/* SPD Table filter flags */
enum xfrm_spd_ftype_t {
XFRM_SPD_UNSPEC,
XFRM_SPD_HMASK=1,
XFRM_SPD_HMAX=2,
XFRM_SPD_ICNT=4,
XFRM_SPD_OCNT=8,
XFRM_SPD_FCNT=16,
XFRM_SPD_ISCNT=32,
XFRM_SPD_OSCNT=64,
XFRM_SPD_FSCNT=128,
__XFRM_SPD_MAX
#define XFRM_SPD_MAX (__XFRM_SPD_MAX - 1)
};
enum xfrm_spdattr_type_t {
XFRMA_SPD_UNSPEC,
XFRMA_SPDHMASK,
XFRMA_SPDHMAX,
XFRMA_SPDICNT,
XFRMA_SPDOCNT,
XFRMA_SPDFCNT,
XFRMA_SPDISCNT,
XFRMA_SPDOSCNT,
XFRMA_SPDFSCNT,
__XFRMA_SPD_MAX
#define XFRMA_SPD_MAX (__XFRMA_SPD_MAX - 1)
};
struct xfrm_usersa_info {
struct xfrm_selector sel;
struct xfrm_id id;
......
......@@ -97,4 +97,10 @@ extern void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
extern void flow_cache_flush(void);
extern atomic_t flow_cache_genid;
static inline int flow_cache_uli_match(struct flowi *fl1, struct flowi *fl2)
{
return (fl1->proto == fl2->proto &&
!memcmp(&fl1->uli_u, &fl2->uli_u, sizeof(fl1->uli_u)));
}
#endif
......@@ -16,7 +16,7 @@
* completed a register, it can exploit the other functions.
* For furthur reference on all IUCV functionality, refer to the
* CP Programming Services book, also available on the web thru
* www.ibm.com/s390/vm/pubs, manual # SC24-5760
* www.vm.ibm.com/pubs, manual # SC24-6084
*
* Definition of Return Codes
* - All positive return codes including zero are reflected back
......
......@@ -736,9 +736,7 @@ static inline __u32 tcp_current_ssthresh(const struct sock *sk)
static inline void tcp_sync_left_out(struct tcp_sock *tp)
{
if (tp->rx_opt.sack_ok &&
(tp->sacked_out >= tp->packets_out - tp->lost_out))
tp->sacked_out = tp->packets_out - tp->lost_out;
BUG_ON(tp->sacked_out + tp->lost_out > tp->packets_out);
tp->left_out = tp->sacked_out + tp->lost_out;
}
......@@ -1201,9 +1199,14 @@ static inline struct sk_buff *tcp_send_head(struct sock *sk)
static inline void tcp_advance_send_head(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
sk->sk_send_head = skb->next;
if (sk->sk_send_head == (struct sk_buff *)&sk->sk_write_queue)
sk->sk_send_head = NULL;
/* Don't override Nagle indefinately with F-RTO */
if (tp->frto_counter == 2)
tp->frto_counter = 3;
}
static inline void tcp_check_send_head(struct sock *sk, struct sk_buff *skb_unlinked)
......
......@@ -423,6 +423,18 @@ struct xfrm_sadinfo
u32 sadhmcnt; /* max allowed hash bkts */
u32 sadcnt; /* current running count */
};
struct xfrm_spdinfo
{
u32 incnt;
u32 outcnt;
u32 fwdcnt;
u32 inscnt;
u32 outscnt;
u32 fwdscnt;
u32 spdhcnt;
u32 spdhmcnt;
};
#ifdef CONFIG_AUDITSYSCALL
extern void xfrm_audit_log(uid_t auid, u32 secid, int type, int result,
struct xfrm_policy *xp, struct xfrm_state *x);
......@@ -591,6 +603,10 @@ struct xfrm_dst
struct rt6_info rt6;
} u;
struct dst_entry *route;
#ifdef CONFIG_XFRM_SUB_POLICY
struct flowi *origin;
struct xfrm_selector *partner;
#endif
u32 genid;
u32 route_mtu_cached;
u32 child_mtu_cached;
......@@ -603,6 +619,12 @@ static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)
dst_release(xdst->route);
if (likely(xdst->u.dst.xfrm))
xfrm_state_put(xdst->u.dst.xfrm);
#ifdef CONFIG_XFRM_SUB_POLICY
kfree(xdst->origin);
xdst->origin = NULL;
kfree(xdst->partner);
xdst->partner = NULL;
#endif
}
extern void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev);
......@@ -946,6 +968,7 @@ extern struct xfrm_state *xfrm_find_acq_byseq(u32 seq);
extern int xfrm_state_delete(struct xfrm_state *x);
extern void xfrm_state_flush(u8 proto, struct xfrm_audit *audit_info);
extern void xfrm_sad_getinfo(struct xfrm_sadinfo *si);
extern void xfrm_spd_getinfo(struct xfrm_spdinfo *si);
extern int xfrm_replay_check(struct xfrm_state *x, __be32 seq);
extern void xfrm_replay_advance(struct xfrm_state *x, __be32 seq);
extern void xfrm_replay_notify(struct xfrm_state *x, int event);
......
......@@ -2101,26 +2101,23 @@ static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
{
struct net_device_stats *stats = dev->get_stats(dev);
if (stats) {
seq_printf(seq, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu "
"%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
dev->name, stats->rx_bytes, stats->rx_packets,
stats->rx_errors,
stats->rx_dropped + stats->rx_missed_errors,
stats->rx_fifo_errors,
stats->rx_length_errors + stats->rx_over_errors +
stats->rx_crc_errors + stats->rx_frame_errors,
stats->rx_compressed, stats->multicast,
stats->tx_bytes, stats->tx_packets,
stats->tx_errors, stats->tx_dropped,
stats->tx_fifo_errors, stats->collisions,
stats->tx_carrier_errors +
stats->tx_aborted_errors +
stats->tx_window_errors +
stats->tx_heartbeat_errors,
stats->tx_compressed);
} else
seq_printf(seq, "%6s: No statistics available.\n", dev->name);
seq_printf(seq, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu "
"%8lu %7lu %4lu %4lu %4lu %5lu %7lu %10lu\n",
dev->name, stats->rx_bytes, stats->rx_packets,
stats->rx_errors,
stats->rx_dropped + stats->rx_missed_errors,
stats->rx_fifo_errors,
stats->rx_length_errors + stats->rx_over_errors +
stats->rx_crc_errors + stats->rx_frame_errors,
stats->rx_compressed, stats->multicast,
stats->tx_bytes, stats->tx_packets,
stats->tx_errors, stats->tx_dropped,
stats->tx_fifo_errors, stats->collisions,
stats->tx_carrier_errors +
stats->tx_aborted_errors +
stats->tx_window_errors +
stats->tx_heartbeat_errors,
stats->tx_compressed);
}
/*
......@@ -3257,11 +3254,9 @@ void netdev_run_todo(void)
mutex_unlock(&net_todo_run_mutex);
}
static struct net_device_stats *maybe_internal_stats(struct net_device *dev)
static struct net_device_stats *internal_stats(struct net_device *dev)
{
if (dev->features & NETIF_F_INTERNAL_STATS)
return &dev->stats;
return NULL;
return &dev->stats;
}
/**
......@@ -3299,7 +3294,7 @@ struct net_device *alloc_netdev(int sizeof_priv, const char *name,
if (sizeof_priv)
dev->priv = netdev_priv(dev);
dev->get_stats = maybe_internal_stats;
dev->get_stats = internal_stats;
setup(dev);
strcpy(dev->name, name);
return dev;
......
......@@ -329,6 +329,7 @@ static inline int ip_rcv_options(struct sk_buff *skb)
static inline int ip_rcv_finish(struct sk_buff *skb)
{
const struct iphdr *iph = ip_hdr(skb);
struct rtable *rt;
/*
* Initialise the virtual path cache for the packet. It describes
......@@ -340,6 +341,8 @@ static inline int ip_rcv_finish(struct sk_buff *skb)
if (unlikely(err)) {
if (err == -EHOSTUNREACH)
IP_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
else if (err == -ENETUNREACH)
IP_INC_STATS_BH(IPSTATS_MIB_INNOROUTES);
goto drop;
}
}
......@@ -358,6 +361,12 @@ static inline int ip_rcv_finish(struct sk_buff *skb)
if (iph->ihl > 5 && ip_rcv_options(skb))
goto drop;
rt = (struct rtable*)skb->dst;
if (rt->rt_type == RTN_MULTICAST)
IP_INC_STATS_BH(IPSTATS_MIB_INMCASTPKTS);
else if (rt->rt_type == RTN_BROADCAST)
IP_INC_STATS_BH(IPSTATS_MIB_INBCASTPKTS);
return dst_input(skb);
drop:
......@@ -414,7 +423,10 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
goto inhdr_error;
len = ntohs(iph->tot_len);
if (skb->len < len || len < (iph->ihl*4))
if (skb->len < len) {
IP_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS);
goto drop;
} else if (len < (iph->ihl*4))
goto inhdr_error;
/* Our transport medium may have padded the buffer out. Now we know it
......
......@@ -160,9 +160,15 @@ EXPORT_SYMBOL_GPL(ip_build_and_send_pkt);
static inline int ip_finish_output2(struct sk_buff *skb)
{
struct dst_entry *dst = skb->dst;
struct rtable *rt = (struct rtable *)dst;
struct net_device *dev = dst->dev;
int hh_len = LL_RESERVED_SPACE(dev);
if (rt->rt_type == RTN_MULTICAST)
IP_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS);
else if (rt->rt_type == RTN_BROADCAST)
IP_INC_STATS(IPSTATS_MIB_OUTBCASTPKTS);
/* Be paranoid, rather than too clever. */
if (unlikely(skb_headroom(skb) < hh_len && dev->hard_header)) {
struct sk_buff *skb2;
......
......@@ -1573,14 +1573,12 @@ void tcp_close(struct sock *sk, long timeout)
sk_stream_mem_reclaim(sk);
/* As outlined in draft-ietf-tcpimpl-prob-03.txt, section
* 3.10, we send a RST here because data was lost. To
* witness the awful effects of the old behavior of always
* doing a FIN, run an older 2.1.x kernel or 2.0.x, start
* a bulk GET in an FTP client, suspend the process, wait
* for the client to advertise a zero window, then kill -9
* the FTP client, wheee... Note: timeout is always zero
* in such a case.
/* As outlined in RFC 2525, section 2.17, we send a RST here because
* data was lost. To witness the awful effects of the old behavior of
* always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
* GET in an FTP client, suspend the process, wait for the client to
* advertise a zero window, then kill -9 the FTP client, wheee...
* Note: timeout is always zero in such a case.
*/
if (data_was_unread) {
/* Unread data was tossed, zap the connection. */
......
......@@ -1265,20 +1265,15 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
return flag;
}
/* F-RTO can only be used if these conditions are satisfied:
* - there must be some unsent new data
* - the advertised window should allow sending it
* - TCP has never retransmitted anything other than head (SACK enhanced
* variant from Appendix B of RFC4138 is more robust here)
/* F-RTO can only be used if TCP has never retransmitted anything other than
* head (SACK enhanced variant from Appendix B of RFC4138 is more robust here)
*/
int tcp_use_frto(struct sock *sk)
{
const struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
if (!sysctl_tcp_frto || !tcp_send_head(sk) ||
after(TCP_SKB_CB(tcp_send_head(sk))->end_seq,
tp->snd_una + tp->snd_wnd))
if (!sysctl_tcp_frto)
return 0;
if (IsSackFrto())
......@@ -2642,7 +2637,9 @@ static void tcp_undo_spur_to_response(struct sock *sk, int flag)
* algorithm is not part of the F-RTO detection algorithm
* given in RFC4138 but can be selected separately).
* Otherwise (basically on duplicate ACK), RTO was (likely) caused by a loss
* and TCP falls back to conventional RTO recovery.
* and TCP falls back to conventional RTO recovery. F-RTO allows overriding
* of Nagle, this is done using frto_counter states 2 and 3, when a new data
* segment of any size sent during F-RTO, state 2 is upgraded to 3.
*
* Rationale: if the RTO was spurious, new ACKs should arrive from the
* original window even after we transmit two new data segments.
......@@ -2671,7 +2668,7 @@ static int tcp_process_frto(struct sock *sk, u32 prior_snd_una, int flag)
inet_csk(sk)->icsk_retransmits = 0;
if (!before(tp->snd_una, tp->frto_highmark)) {
tcp_enter_frto_loss(sk, tp->frto_counter + 1, flag);
tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 2 : 3), flag);
return 1;
}
......@@ -2697,7 +2694,7 @@ static int tcp_process_frto(struct sock *sk, u32 prior_snd_una, int flag)
return 1;
}
if ((tp->frto_counter == 2) &&
if ((tp->frto_counter >= 2) &&
(!(flag&FLAG_FORWARD_PROGRESS) ||
((flag&FLAG_DATA_SACKED) && !(flag&FLAG_ONLY_ORIG_SACKED)))) {
/* RFC4138 shortcoming (see comment above) */
......@@ -2710,10 +2707,19 @@ static int tcp_process_frto(struct sock *sk, u32 prior_snd_una, int flag)
}
if (tp->frto_counter == 1) {
/* Sending of the next skb must be allowed or no FRTO */
if (!tcp_send_head(sk) ||
after(TCP_SKB_CB(tcp_send_head(sk))->end_seq,
tp->snd_una + tp->snd_wnd)) {
tcp_enter_frto_loss(sk, (tp->frto_counter == 1 ? 2 : 3),
flag);
return 1;
}
tp->snd_cwnd = tcp_packets_in_flight(tp) + 2;
tp->frto_counter = 2;
return 1;
} else /* frto_counter == 2 */ {
} else {
switch (sysctl_tcp_frto_response) {
case 2:
tcp_undo_spur_to_response(sk, flag);
......
......@@ -1035,8 +1035,10 @@ static inline int tcp_nagle_test(struct tcp_sock *tp, struct sk_buff *skb,
if (nonagle & TCP_NAGLE_PUSH)
return 1;
/* Don't use the nagle rule for urgent data (or for the final FIN). */
if (tp->urg_mode ||
/* Don't use the nagle rule for urgent data (or for the final FIN).
* Nagle can be ignored during F-RTO too (see RFC4138).
*/
if (tp->urg_mode || (tp->frto_counter == 2) ||
(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN))
return 1;
......@@ -2035,7 +2037,7 @@ void tcp_send_fin(struct sock *sk)
/* We get here when a process closes a file descriptor (either due to
* an explicit close() or as a byproduct of exit()'ing) and there
* was unread data in the receive queue. This behavior is recommended
* by draft-ietf-tcpimpl-prob-03.txt section 3.10. -DaveM
* by RFC 2525, section 2.17. -DaveM
*/
void tcp_send_active_reset(struct sock *sk, gfp_t priority)
{
......
......@@ -114,14 +114,33 @@ DEFINE_RWLOCK(udp_hash_lock);
static int udp_port_rover;
static inline int __udp_lib_lport_inuse(__u16 num, struct hlist_head udptable[])
/*
* Note about this hash function :
* Typical use is probably daddr = 0, only dport is going to vary hash
*/
static inline unsigned int hash_port_and_addr(__u16 port, __be32 addr)
{
addr ^= addr >> 16;
addr ^= addr >> 8;
return port ^ addr;
}
static inline int __udp_lib_port_inuse(unsigned int hash, int port,
__be32 daddr, struct hlist_head udptable[])
{
struct sock *sk;
struct hlist_node *node;
struct inet_sock *inet;
sk_for_each(sk, node, &udptable[num & (UDP_HTABLE_SIZE - 1)])
if (sk->sk_hash == num)
sk_for_each(sk, node, &udptable[hash & (UDP_HTABLE_SIZE - 1)]) {
if (sk->sk_hash != hash)
continue;
inet = inet_sk(sk);
if (inet->num != port)
continue;
if (inet->rcv_saddr == daddr)
return 1;
}
return 0;
}
......@@ -142,6 +161,7 @@ int __udp_lib_get_port(struct sock *sk, unsigned short snum,
struct hlist_node *node;
struct hlist_head *head;
struct sock *sk2;
unsigned int hash;
int error = 1;
write_lock_bh(&udp_hash_lock);
......@@ -156,7 +176,9 @@ int __udp_lib_get_port(struct sock *sk, unsigned short snum,
for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
int size;
head = &udptable[result & (UDP_HTABLE_SIZE - 1)];
hash = hash_port_and_addr(result,
inet_sk(sk)->rcv_saddr);
head = &udptable[hash & (UDP_HTABLE_SIZE - 1)];
if (hlist_empty(head)) {
if (result > sysctl_local_port_range[1])
result = sysctl_local_port_range[0] +
......@@ -181,7 +203,10 @@ int __udp_lib_get_port(struct sock *sk, unsigned short snum,
result = sysctl_local_port_range[0]
+ ((result - sysctl_local_port_range[0]) &
(UDP_HTABLE_SIZE - 1));
if (! __udp_lib_lport_inuse(result, udptable))
hash = hash_port_and_addr(result,
inet_sk(sk)->rcv_saddr);
if (! __udp_lib_port_inuse(hash, result,
inet_sk(sk)->rcv_saddr, udptable))
break;
}
if (i >= (1 << 16) / UDP_HTABLE_SIZE)
......@@ -189,11 +214,13 @@ int __udp_lib_get_port(struct sock *sk, unsigned short snum,
gotit:
*port_rover = snum = result;
} else {
head = &udptable[snum & (UDP_HTABLE_SIZE - 1)];
hash = hash_port_and_addr(snum, inet_sk(sk)->rcv_saddr);
head = &udptable[hash & (UDP_HTABLE_SIZE - 1)];
sk_for_each(sk2, node, head)
if (sk2->sk_hash == snum &&
if (sk2->sk_hash == hash &&
sk2 != sk &&
inet_sk(sk2)->num == snum &&
(!sk2->sk_reuse || !sk->sk_reuse) &&
(!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if
|| sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
......@@ -201,9 +228,9 @@ int __udp_lib_get_port(struct sock *sk, unsigned short snum,
goto fail;
}
inet_sk(sk)->num = snum;
sk->sk_hash = snum;
sk->sk_hash = hash;
if (sk_unhashed(sk)) {
head = &udptable[snum & (UDP_HTABLE_SIZE - 1)];
head = &udptable[hash & (UDP_HTABLE_SIZE - 1)];
sk_add_node(sk, head);
sock_prot_inc_use(sk->sk_prot);
}
......@@ -242,63 +269,78 @@ static struct sock *__udp4_lib_lookup(__be32 saddr, __be16 sport,
{
struct sock *sk, *result = NULL;
struct hlist_node *node;
unsigned short hnum = ntohs(dport);
int badness = -1;
unsigned int hash, hashwild;
int score, best = -1;
hash = hash_port_and_addr(ntohs(dport), daddr);
hashwild = hash_port_and_addr(ntohs(dport), 0);
read_lock(&udp_hash_lock);
sk_for_each(sk, node, &udptable[hnum & (UDP_HTABLE_SIZE - 1)]) {
lookup:
sk_for_each(sk, node, &udptable[hash & (UDP_HTABLE_SIZE - 1)]) {
struct inet_sock *inet = inet_sk(sk);
if (sk->sk_hash == hnum && !ipv6_only_sock(sk)) {
int score = (sk->sk_family == PF_INET ? 1 : 0);
if (inet->rcv_saddr) {
if (inet->rcv_saddr != daddr)
continue;
score+=2;
}
if (inet->daddr) {
if (inet->daddr != saddr)
continue;
score+=2;
}
if (inet->dport) {
if (inet->dport != sport)
continue;
score+=2;
}
if (sk->sk_bound_dev_if) {
if (sk->sk_bound_dev_if != dif)
continue;
score+=2;
}
if (score == 9) {
result = sk;
break;
} else if (score > badness) {
result = sk;
badness = score;
}
if (sk->sk_hash != hash || ipv6_only_sock(sk) ||
inet->num != dport)
continue;
score = (sk->sk_family == PF_INET ? 1 : 0);
if (inet->rcv_saddr) {
if (inet->rcv_saddr != daddr)
continue;
score+=2;
}
if (inet->daddr) {
if (inet->daddr != saddr)
continue;
score+=2;
}
if (inet->dport) {
if (inet->dport != sport)
continue;
score+=2;
}
if (sk->sk_bound_dev_if) {
if (sk->sk_bound_dev_if != dif)
continue;
score+=2;
}
if (score == 9) {
result = sk;
goto found;
} else if (score > best) {
result = sk;
best = score;
}
}
if (hash != hashwild) {
hash = hashwild;
goto lookup;
}
found:
if (result)
sock_hold(result);
read_unlock(&udp_hash_lock);
return result;
}
static inline struct sock *udp_v4_mcast_next(struct sock *sk,
__be16 loc_port, __be32 loc_addr,
__be16 rmt_port, __be32 rmt_addr,
int dif)
static inline struct sock *udp_v4_mcast_next(
struct sock *sk,
unsigned int hnum, __be16 loc_port, __be32 loc_addr,
__be16 rmt_port, __be32 rmt_addr,
int dif)
{
struct hlist_node *node;
struct sock *s = sk;
unsigned short hnum = ntohs(loc_port);
sk_for_each_from(s, node) {
struct inet_sock *inet = inet_sk(s);
if (s->sk_hash != hnum ||
inet->num != loc_port ||
(inet->daddr && inet->daddr != rmt_addr) ||
(inet->dport != rmt_port && inet->dport) ||
(inet->rcv_saddr && inet->rcv_saddr != loc_addr) ||
......@@ -1129,29 +1171,44 @@ static int __udp4_lib_mcast_deliver(struct sk_buff *skb,
__be32 saddr, __be32 daddr,
struct hlist_head udptable[])
{
struct sock *sk;
struct sock *sk, *skw, *sknext;
int dif;
unsigned int hash = hash_port_and_addr(ntohs(uh->dest), daddr);
unsigned int hashwild = hash_port_and_addr(ntohs(uh->dest), 0);
read_lock(&udp_hash_lock);
sk = sk_head(&udptable[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)]);
dif = skb->dev->ifindex;
sk = udp_v4_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif);
if (sk) {
struct sock *sknext = NULL;
read_lock(&udp_hash_lock);
sk = sk_head(&udptable[hash & (UDP_HTABLE_SIZE - 1)]);
skw = sk_head(&udptable[hashwild & (UDP_HTABLE_SIZE - 1)]);
sk = udp_v4_mcast_next(sk, hash, uh->dest, daddr, uh->source, saddr, dif);
if (!sk) {
hash = hashwild;
sk = udp_v4_mcast_next(skw, hash, uh->dest, daddr, uh->source,
saddr, dif);
}
if (sk) {
do {
struct sk_buff *skb1 = skb;
sknext = udp_v4_mcast_next(sk_next(sk), uh->dest, daddr,
uh->source, saddr, dif);
sknext = udp_v4_mcast_next(sk_next(sk), hash, uh->dest,
daddr, uh->source, saddr, dif);
if (!sknext && hash != hashwild) {
hash = hashwild;
sknext = udp_v4_mcast_next(skw, hash, uh->dest,
daddr, uh->source, saddr, dif);
}
if (sknext)
skb1 = skb_clone(skb, GFP_ATOMIC);
if (skb1) {
int ret = udp_queue_rcv_skb(sk, skb1);
if (ret > 0)
/* we should probably re-process instead
* of dropping packets here. */
/*
* we should probably re-process
* instead of dropping packets here.
*/
kfree_skb(skb1);
}
sk = sknext;
......
......@@ -2359,8 +2359,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
break;
case NETDEV_CHANGENAME:
#ifdef CONFIG_SYSCTL
if (idev) {
snmp6_unregister_dev(idev);
#ifdef CONFIG_SYSCTL
addrconf_sysctl_unregister(&idev->cnf);
neigh_sysctl_unregister(idev->nd_parms);
neigh_sysctl_register(dev, idev->nd_parms,
......@@ -2368,8 +2369,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
&ndisc_ifinfo_sysctl_change,
NULL);
addrconf_sysctl_register(idev, &idev->cnf);
}
#endif
snmp6_register_dev(idev);
}
break;
}
......
......@@ -223,6 +223,7 @@ int snmp6_unregister_dev(struct inet6_dev *idev)
return -EINVAL;
remove_proc_entry(idev->stats.proc_dir_entry->name,
proc_net_devsnmp6);
idev->stats.proc_dir_entry = NULL;
return 0;
}
......
......@@ -261,7 +261,7 @@ static int xfrm6_tunnel_rcv(struct sk_buff *skb)
__be32 spi;
spi = xfrm6_tunnel_spi_lookup((xfrm_address_t *)&iph->saddr);
return xfrm6_rcv_spi(skb, spi);
return xfrm6_rcv_spi(skb, spi) > 0 ? : 0;
}
static int xfrm6_tunnel_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
......
......@@ -90,20 +90,43 @@ struct iucv_irq_data {
u32 res2[8];
};
struct iucv_work {
struct iucv_irq_list {
struct list_head list;
struct iucv_irq_data data;
};
static LIST_HEAD(iucv_work_queue);
static DEFINE_SPINLOCK(iucv_work_lock);
static struct iucv_irq_data *iucv_irq_data;
static cpumask_t iucv_buffer_cpumask = CPU_MASK_NONE;
static cpumask_t iucv_irq_cpumask = CPU_MASK_NONE;
static void iucv_tasklet_handler(unsigned long);
static DECLARE_TASKLET(iucv_tasklet, iucv_tasklet_handler,0);
/*
* Queue of interrupt buffers lock for delivery via the tasklet
* (fast but can't call smp_call_function).
*/
static LIST_HEAD(iucv_task_queue);
/*
* The tasklet for fast delivery of iucv interrupts.
*/
static void iucv_tasklet_fn(unsigned long);
static DECLARE_TASKLET(iucv_tasklet, iucv_tasklet_fn,0);
/*
* Queue of interrupt buffers for delivery via a work queue
* (slower but can call smp_call_function).
*/
static LIST_HEAD(iucv_work_queue);
/*
* The work element to deliver path pending interrupts.
*/
static void iucv_work_fn(struct work_struct *work);
static DECLARE_WORK(iucv_work, iucv_work_fn);
/*
* Spinlock protecting task and work queue.
*/
static DEFINE_SPINLOCK(iucv_queue_lock);
enum iucv_command_codes {
IUCV_QUERY = 0,
......@@ -147,10 +170,10 @@ static unsigned long iucv_max_pathid;
static DEFINE_SPINLOCK(iucv_table_lock);
/*
* iucv_tasklet_cpu: contains the number of the cpu executing the tasklet.
* Needed for iucv_path_sever called from tasklet.
* iucv_active_cpu: contains the number of the cpu executing the tasklet
* or the work handler. Needed for iucv_path_sever called from tasklet.
*/
static int iucv_tasklet_cpu = -1;
static int iucv_active_cpu = -1;
/*
* Mutex and wait queue for iucv_register/iucv_unregister.
......@@ -449,17 +472,19 @@ static void iucv_setmask_mp(void)
{
int cpu;
preempt_disable();
for_each_online_cpu(cpu)
/* Enable all cpus with a declared buffer. */
if (cpu_isset(cpu, iucv_buffer_cpumask) &&
!cpu_isset(cpu, iucv_irq_cpumask))
smp_call_function_on(iucv_allow_cpu, NULL, 0, 1, cpu);
preempt_enable();
}
/**
* iucv_setmask_up
*
* Allow iucv interrupts on a single cpus.
* Allow iucv interrupts on a single cpu.
*/
static void iucv_setmask_up(void)
{
......@@ -493,8 +518,10 @@ static int iucv_enable(void)
goto out;
/* Declare per cpu buffers. */
rc = -EIO;
preempt_disable();
for_each_online_cpu(cpu)
smp_call_function_on(iucv_declare_cpu, NULL, 0, 1, cpu);
preempt_enable();
if (cpus_empty(iucv_buffer_cpumask))
/* No cpu could declare an iucv buffer. */
goto out_path;
......@@ -584,48 +611,49 @@ static int iucv_sever_pathid(u16 pathid, u8 userdata[16])
return iucv_call_b2f0(IUCV_SEVER, parm);
}
#ifdef CONFIG_SMP
/**
* __iucv_cleanup_pathid
* __iucv_cleanup_queue
* @dummy: unused dummy argument
*
* Nop function called via smp_call_function to force work items from
* pending external iucv interrupts to the work queue.
*/
static void __iucv_cleanup_pathid(void *dummy)
static void __iucv_cleanup_queue(void *dummy)
{
}
#endif
/**
* iucv_cleanup_pathid
* @pathid: 16 bit pathid
* iucv_cleanup_queue
*
* Function called after a path has been severed to find all remaining
* work items for the now stale pathid. The caller needs to hold the
* iucv_table_lock.
*/
static void iucv_cleanup_pathid(u16 pathid)
static void iucv_cleanup_queue(void)
{
struct iucv_work *p, *n;
struct iucv_irq_list *p, *n;
/*
* Path is severed, the pathid can be reused immediatly on
* a iucv connect or a connection pending interrupt.
* iucv_path_connect and connection pending interrupt will
* wait until the iucv_table_lock is released before the
* recycled pathid enters the system.
* Force remaining interrupts to the work queue, then
* scan the work queue for items of this path.
* When a path is severed, the pathid can be reused immediatly
* on a iucv connect or a connection pending interrupt. Remove
* all entries from the task queue that refer to a stale pathid
* (iucv_path_table[ix] == NULL). Only then do the iucv connect
* or deliver the connection pending interrupt. To get all the
* pending interrupts force them to the work queue by calling
* an empty function on all cpus.
*/
smp_call_function(__iucv_cleanup_pathid, NULL, 0, 1);
spin_lock_irq(&iucv_work_lock);
list_for_each_entry_safe(p, n, &iucv_work_queue, list) {
/* Remove work items for pathid except connection pending */
if (p->data.ippathid == pathid && p->data.iptype != 0x01) {
smp_call_function(__iucv_cleanup_queue, NULL, 0, 1);
spin_lock_irq(&iucv_queue_lock);
list_for_each_entry_safe(p, n, &iucv_task_queue, list) {
/* Remove stale work items from the task queue. */
if (iucv_path_table[p->data.ippathid] == NULL) {
list_del(&p->list);
kfree(p);
}
}
spin_unlock_irq(&iucv_work_lock);
spin_unlock_irq(&iucv_queue_lock);
}
/**
......@@ -684,7 +712,6 @@ void iucv_unregister(struct iucv_handler *handler, int smp)
iucv_sever_pathid(p->pathid, NULL);
iucv_path_table[p->pathid] = NULL;
list_del(&p->list);
iucv_cleanup_pathid(p->pathid);
iucv_path_free(p);
}
spin_unlock_bh(&iucv_table_lock);
......@@ -757,9 +784,9 @@ int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler,
union iucv_param *parm;
int rc;
preempt_disable();
if (iucv_tasklet_cpu != smp_processor_id())
spin_lock_bh(&iucv_table_lock);
BUG_ON(in_atomic());
spin_lock_bh(&iucv_table_lock);
iucv_cleanup_queue();
parm = percpu_ptr(iucv_param, smp_processor_id());
memset(parm, 0, sizeof(union iucv_param));
parm->ctrl.ipmsglim = path->msglim;
......@@ -794,9 +821,7 @@ int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler,
rc = -EIO;
}
}
if (iucv_tasklet_cpu != smp_processor_id())
spin_unlock_bh(&iucv_table_lock);
preempt_enable();
spin_unlock_bh(&iucv_table_lock);
return rc;
}
......@@ -867,15 +892,14 @@ int iucv_path_sever(struct iucv_path *path, u8 userdata[16])
preempt_disable();
if (iucv_tasklet_cpu != smp_processor_id())
if (iucv_active_cpu != smp_processor_id())
spin_lock_bh(&iucv_table_lock);
rc = iucv_sever_pathid(path->pathid, userdata);
if (!rc) {
iucv_path_table[path->pathid] = NULL;
list_del_init(&path->list);
iucv_cleanup_pathid(path->pathid);
}
if (iucv_tasklet_cpu != smp_processor_id())
if (iucv_active_cpu != smp_processor_id())
spin_unlock_bh(&iucv_table_lock);
preempt_enable();
return rc;
......@@ -1244,8 +1268,7 @@ static void iucv_path_complete(struct iucv_irq_data *data)
struct iucv_path_complete *ipc = (void *) data;
struct iucv_path *path = iucv_path_table[ipc->ippathid];
BUG_ON(!path || !path->handler);
if (path->handler->path_complete)
if (path && path->handler && path->handler->path_complete)
path->handler->path_complete(path, ipc->ipuser);
}
......@@ -1273,14 +1296,14 @@ static void iucv_path_severed(struct iucv_irq_data *data)
struct iucv_path_severed *ips = (void *) data;
struct iucv_path *path = iucv_path_table[ips->ippathid];
BUG_ON(!path || !path->handler);
if (!path || !path->handler) /* Already severed */
return;
if (path->handler->path_severed)
path->handler->path_severed(path, ips->ipuser);
else {
iucv_sever_pathid(path->pathid, NULL);
iucv_path_table[path->pathid] = NULL;
list_del_init(&path->list);
iucv_cleanup_pathid(path->pathid);
iucv_path_free(path);
}
}
......@@ -1309,8 +1332,7 @@ static void iucv_path_quiesced(struct iucv_irq_data *data)
struct iucv_path_quiesced *ipq = (void *) data;
struct iucv_path *path = iucv_path_table[ipq->ippathid];
BUG_ON(!path || !path->handler);
if (path->handler->path_quiesced)
if (path && path->handler && path->handler->path_quiesced)
path->handler->path_quiesced(path, ipq->ipuser);
}
......@@ -1338,8 +1360,7 @@ static void iucv_path_resumed(struct iucv_irq_data *data)
struct iucv_path_resumed *ipr = (void *) data;
struct iucv_path *path = iucv_path_table[ipr->ippathid];
BUG_ON(!path || !path->handler);
if (path->handler->path_resumed)
if (path && path->handler && path->handler->path_resumed)
path->handler->path_resumed(path, ipr->ipuser);
}
......@@ -1371,8 +1392,7 @@ static void iucv_message_complete(struct iucv_irq_data *data)
struct iucv_path *path = iucv_path_table[imc->ippathid];
struct iucv_message msg;
BUG_ON(!path || !path->handler);
if (path->handler->message_complete) {
if (path && path->handler && path->handler->message_complete) {
msg.flags = imc->ipflags1;
msg.id = imc->ipmsgid;
msg.audit = imc->ipaudit;
......@@ -1417,8 +1437,7 @@ static void iucv_message_pending(struct iucv_irq_data *data)
struct iucv_path *path = iucv_path_table[imp->ippathid];
struct iucv_message msg;
BUG_ON(!path || !path->handler);
if (path->handler->message_pending) {
if (path && path->handler && path->handler->message_pending) {
msg.flags = imp->ipflags1;
msg.id = imp->ipmsgid;
msg.class = imp->iptrgcls;
......@@ -1433,17 +1452,16 @@ static void iucv_message_pending(struct iucv_irq_data *data)
}
/**
* iucv_tasklet_handler:
* iucv_tasklet_fn:
*
* This tasklet loops over the queue of irq buffers created by
* iucv_external_interrupt, calls the appropriate action handler
* and then frees the buffer.
*/
static void iucv_tasklet_handler(unsigned long ignored)
static void iucv_tasklet_fn(unsigned long ignored)
{
typedef void iucv_irq_fn(struct iucv_irq_data *);
static iucv_irq_fn *irq_fn[] = {
[0x01] = iucv_path_pending,
[0x02] = iucv_path_complete,
[0x03] = iucv_path_severed,
[0x04] = iucv_path_quiesced,
......@@ -1453,38 +1471,70 @@ static void iucv_tasklet_handler(unsigned long ignored)
[0x08] = iucv_message_pending,
[0x09] = iucv_message_pending,
};
struct iucv_work *p;
struct list_head task_queue = LIST_HEAD_INIT(task_queue);
struct iucv_irq_list *p, *n;
/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
spin_lock(&iucv_table_lock);
iucv_tasklet_cpu = smp_processor_id();
iucv_active_cpu = smp_processor_id();
spin_lock_irq(&iucv_work_lock);
while (!list_empty(&iucv_work_queue)) {
p = list_entry(iucv_work_queue.next, struct iucv_work, list);
spin_lock_irq(&iucv_queue_lock);
list_splice_init(&iucv_task_queue, &task_queue);
spin_unlock_irq(&iucv_queue_lock);
list_for_each_entry_safe(p, n, &task_queue, list) {
list_del_init(&p->list);
spin_unlock_irq(&iucv_work_lock);
irq_fn[p->data.iptype](&p->data);
kfree(p);
spin_lock_irq(&iucv_work_lock);
}
spin_unlock_irq(&iucv_work_lock);
iucv_tasklet_cpu = -1;
iucv_active_cpu = -1;
spin_unlock(&iucv_table_lock);
}
/**
* iucv_work_fn:
*
* This work function loops over the queue of path pending irq blocks
* created by iucv_external_interrupt, calls the appropriate action
* handler and then frees the buffer.
*/
static void iucv_work_fn(struct work_struct *work)
{
typedef void iucv_irq_fn(struct iucv_irq_data *);
struct list_head work_queue = LIST_HEAD_INIT(work_queue);
struct iucv_irq_list *p, *n;
/* Serialize tasklet, iucv_path_sever and iucv_path_connect. */
spin_lock_bh(&iucv_table_lock);
iucv_active_cpu = smp_processor_id();
spin_lock_irq(&iucv_queue_lock);
list_splice_init(&iucv_work_queue, &work_queue);
spin_unlock_irq(&iucv_queue_lock);
iucv_cleanup_queue();
list_for_each_entry_safe(p, n, &work_queue, list) {
list_del_init(&p->list);
iucv_path_pending(&p->data);
kfree(p);
}
iucv_active_cpu = -1;
spin_unlock_bh(&iucv_table_lock);
}
/**
* iucv_external_interrupt
* @code: irq code
*
* Handles external interrupts coming in from CP.
* Places the interrupt buffer on a queue and schedules iucv_tasklet_handler().
* Places the interrupt buffer on a queue and schedules iucv_tasklet_fn().
*/
static void iucv_external_interrupt(u16 code)
{
struct iucv_irq_data *p;
struct iucv_work *work;
struct iucv_irq_list *work;
p = percpu_ptr(iucv_irq_data, smp_processor_id());
if (p->ippathid >= iucv_max_pathid) {
......@@ -1498,16 +1548,23 @@ static void iucv_external_interrupt(u16 code)
printk(KERN_ERR "iucv_do_int: unknown iucv interrupt\n");
return;
}
work = kmalloc(sizeof(struct iucv_work), GFP_ATOMIC);
work = kmalloc(sizeof(struct iucv_irq_list), GFP_ATOMIC);
if (!work) {
printk(KERN_WARNING "iucv_external_interrupt: out of memory\n");
return;
}
memcpy(&work->data, p, sizeof(work->data));
spin_lock(&iucv_work_lock);
list_add_tail(&work->list, &iucv_work_queue);
spin_unlock(&iucv_work_lock);
tasklet_schedule(&iucv_tasklet);
spin_lock(&iucv_queue_lock);
if (p->iptype == 0x01) {
/* Path pending interrupt. */
list_add_tail(&work->list, &iucv_work_queue);
schedule_work(&iucv_work);
} else {
/* The other interrupts. */
list_add_tail(&work->list, &iucv_task_queue);
tasklet_schedule(&iucv_tasklet);
}
spin_unlock(&iucv_queue_lock);
}
/**
......@@ -1577,12 +1634,14 @@ static int iucv_init(void)
*/
static void iucv_exit(void)
{
struct iucv_work *p, *n;
struct iucv_irq_list *p, *n;
spin_lock_irq(&iucv_work_lock);
spin_lock_irq(&iucv_queue_lock);
list_for_each_entry_safe(p, n, &iucv_task_queue, list)
kfree(p);
list_for_each_entry_safe(p, n, &iucv_work_queue, list)
kfree(p);
spin_unlock_irq(&iucv_work_lock);
spin_unlock_irq(&iucv_queue_lock);
unregister_hotcpu_notifier(&iucv_cpu_notifier);
percpu_free(iucv_param);
percpu_free(iucv_irq_data);
......
......@@ -3987,7 +3987,7 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
memcpy(&temp, &from->ipaddr, sizeof(temp));
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len;
if(space_left < addrlen)
if (space_left < addrlen)
return -ENOMEM;
if (copy_to_user(to, &temp, addrlen))
return -EFAULT;
......@@ -4076,8 +4076,9 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
/* Helper function that copies local addresses to user and returns the number
* of addresses copied.
*/
static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs,
void __user *to)
static int sctp_copy_laddrs_old(struct sock *sk, __u16 port,
int max_addrs, void *to,
int *bytes_copied)
{
struct list_head *pos, *next;
struct sctp_sockaddr_entry *addr;
......@@ -4094,10 +4095,10 @@ static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_add
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
&temp);
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
if (copy_to_user(to, &temp, addrlen))
return -EFAULT;
memcpy(to, &temp, addrlen);
to += addrlen;
*bytes_copied += addrlen;
cnt ++;
if (cnt >= max_addrs) break;
}
......@@ -4105,8 +4106,8 @@ static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_add
return cnt;
}
static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port,
void __user **to, size_t space_left)
static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
size_t space_left, int *bytes_copied)
{
struct list_head *pos, *next;
struct sctp_sockaddr_entry *addr;
......@@ -4123,14 +4124,14 @@ static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port,
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
&temp);
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
if(space_left<addrlen)
if (space_left < addrlen)
return -ENOMEM;
if (copy_to_user(*to, &temp, addrlen))
return -EFAULT;
memcpy(to, &temp, addrlen);
*to += addrlen;
to += addrlen;
cnt ++;
space_left -= addrlen;
bytes_copied += addrlen;
}
return cnt;
......@@ -4154,6 +4155,8 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
int addrlen;
rwlock_t *addr_lock;
int err = 0;
void *addrs;
int bytes_copied = 0;
if (len != sizeof(struct sctp_getaddrs_old))
return -EINVAL;
......@@ -4181,6 +4184,15 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
to = getaddrs.addrs;
/* Allocate space for a local instance of packed array to hold all
* the data. We store addresses here first and then put write them
* to the user in one shot.
*/
addrs = kmalloc(sizeof(union sctp_addr) * getaddrs.addr_num,
GFP_KERNEL);
if (!addrs)
return -ENOMEM;
sctp_read_lock(addr_lock);
/* If the endpoint is bound to 0.0.0.0 or ::0, get the valid
......@@ -4190,13 +4202,9 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
addr = list_entry(bp->address_list.next,
struct sctp_sockaddr_entry, list);
if (sctp_is_any(&addr->a)) {
cnt = sctp_copy_laddrs_to_user_old(sk, bp->port,
getaddrs.addr_num,
to);
if (cnt < 0) {
err = cnt;
goto unlock;
}
cnt = sctp_copy_laddrs_old(sk, bp->port,
getaddrs.addr_num,
addrs, &bytes_copied);
goto copy_getaddrs;
}
}
......@@ -4206,22 +4214,29 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
memcpy(&temp, &addr->a, sizeof(temp));
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
if (copy_to_user(to, &temp, addrlen)) {
err = -EFAULT;
goto unlock;
}
memcpy(addrs, &temp, addrlen);
to += addrlen;
bytes_copied += addrlen;
cnt ++;
if (cnt >= getaddrs.addr_num) break;
}
copy_getaddrs:
sctp_read_unlock(addr_lock);
/* copy the entire address list into the user provided space */
if (copy_to_user(to, addrs, bytes_copied)) {
err = -EFAULT;
goto error;
}
/* copy the leading structure back to user */
getaddrs.addr_num = cnt;
if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
err = -EFAULT;
unlock:
sctp_read_unlock(addr_lock);
error:
kfree(addrs);
return err;
}
......@@ -4241,7 +4256,8 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
rwlock_t *addr_lock;
int err = 0;
size_t space_left;
int bytes_copied;
int bytes_copied = 0;
void *addrs;
if (len <= sizeof(struct sctp_getaddrs))
return -EINVAL;
......@@ -4269,6 +4285,9 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
to = optval + offsetof(struct sctp_getaddrs,addrs);
space_left = len - sizeof(struct sctp_getaddrs) -
offsetof(struct sctp_getaddrs,addrs);
addrs = kmalloc(space_left, GFP_KERNEL);
if (!addrs)
return -ENOMEM;
sctp_read_lock(addr_lock);
......@@ -4279,11 +4298,11 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
addr = list_entry(bp->address_list.next,
struct sctp_sockaddr_entry, list);
if (sctp_is_any(&addr->a)) {
cnt = sctp_copy_laddrs_to_user(sk, bp->port,
&to, space_left);
cnt = sctp_copy_laddrs(sk, bp->port, addrs,
space_left, &bytes_copied);
if (cnt < 0) {
err = cnt;
goto unlock;
goto error;
}
goto copy_getaddrs;
}
......@@ -4294,26 +4313,31 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
memcpy(&temp, &addr->a, sizeof(temp));
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
if(space_left < addrlen)
return -ENOMEM; /*fixme: right error?*/
if (copy_to_user(to, &temp, addrlen)) {
err = -EFAULT;
goto unlock;
if (space_left < addrlen) {
err = -ENOMEM; /*fixme: right error?*/
goto error;
}
memcpy(addrs, &temp, addrlen);
to += addrlen;
bytes_copied += addrlen;
cnt ++;
space_left -= addrlen;
}
copy_getaddrs:
sctp_read_unlock(addr_lock);
if (copy_to_user(to, addrs, bytes_copied)) {
err = -EFAULT;
goto error;
}
if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
return -EFAULT;
bytes_copied = ((char __user *)to) - optval;
if (put_user(bytes_copied, optlen))
return -EFAULT;
unlock:
sctp_read_unlock(addr_lock);
error:
kfree(addrs);
return err;
}
......
......@@ -579,8 +579,22 @@ static inline int xfrm_byidx_should_resize(int total)
return 0;
}
static DEFINE_MUTEX(hash_resize_mutex);
void xfrm_spd_getinfo(struct xfrm_spdinfo *si)
{
read_lock_bh(&xfrm_policy_lock);
si->incnt = xfrm_policy_count[XFRM_POLICY_IN];
si->outcnt = xfrm_policy_count[XFRM_POLICY_OUT];
si->fwdcnt = xfrm_policy_count[XFRM_POLICY_FWD];
si->inscnt = xfrm_policy_count[XFRM_POLICY_IN+XFRM_POLICY_MAX];
si->outscnt = xfrm_policy_count[XFRM_POLICY_OUT+XFRM_POLICY_MAX];
si->fwdscnt = xfrm_policy_count[XFRM_POLICY_FWD+XFRM_POLICY_MAX];
si->spdhcnt = xfrm_idx_hmask;
si->spdhmcnt = xfrm_policy_hashmax;
read_unlock_bh(&xfrm_policy_lock);
}
EXPORT_SYMBOL(xfrm_spd_getinfo);
static DEFINE_MUTEX(hash_resize_mutex);
static void xfrm_hash_resize(struct work_struct *__unused)
{
int dir, total;
......@@ -1330,6 +1344,40 @@ xfrm_bundle_create(struct xfrm_policy *policy, struct xfrm_state **xfrm, int nx,
return err;
}
static int inline
xfrm_dst_alloc_copy(void **target, void *src, int size)
{
if (!*target) {
*target = kmalloc(size, GFP_ATOMIC);
if (!*target)
return -ENOMEM;
}
memcpy(*target, src, size);
return 0;
}
static int inline
xfrm_dst_update_parent(struct dst_entry *dst, struct xfrm_selector *sel)
{
#ifdef CONFIG_XFRM_SUB_POLICY
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
return xfrm_dst_alloc_copy((void **)&(xdst->partner),
sel, sizeof(*sel));
#else
return 0;
#endif
}
static int inline
xfrm_dst_update_origin(struct dst_entry *dst, struct flowi *fl)
{
#ifdef CONFIG_XFRM_SUB_POLICY
struct xfrm_dst *xdst = (struct xfrm_dst *)dst;
return xfrm_dst_alloc_copy((void **)&(xdst->origin), fl, sizeof(*fl));
#else
return 0;
#endif
}
static int stale_bundle(struct dst_entry *dst);
......@@ -1518,6 +1566,18 @@ int xfrm_lookup(struct dst_entry **dst_p, struct flowi *fl,
err = -EHOSTUNREACH;
goto error;
}
if (npols > 1)
err = xfrm_dst_update_parent(dst, &pols[1]->selector);
else
err = xfrm_dst_update_origin(dst, fl);
if (unlikely(err)) {
write_unlock_bh(&policy->lock);
if (dst)
dst_free(dst);
goto error;
}
dst->next = policy->bundles;
policy->bundles = dst;
dst_hold(dst);
......@@ -1933,6 +1993,15 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
if (!dst_check(dst->path, ((struct xfrm_dst *)dst)->path_cookie) ||
(dst->dev && !netif_running(dst->dev)))
return 0;
#ifdef CONFIG_XFRM_SUB_POLICY
if (fl) {
if (first->origin && !flow_cache_uli_match(first->origin, fl))
return 0;
if (first->partner &&
!xfrm_selector_match(first->partner, fl, family))
return 0;
}
#endif
last = NULL;
......
......@@ -672,6 +672,81 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
return skb;
}
static int build_spdinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags)
{
struct xfrm_spdinfo si;
struct nlmsghdr *nlh;
u32 *f;
nlh = nlmsg_put(skb, pid, seq, XFRM_MSG_NEWSPDINFO, sizeof(u32), 0);
if (nlh == NULL) /* shouldnt really happen ... */
return -EMSGSIZE;
f = nlmsg_data(nlh);
*f = flags;
xfrm_spd_getinfo(&si);
if (flags & XFRM_SPD_HMASK)
NLA_PUT_U32(skb, XFRMA_SPDHMASK, si.spdhcnt);
if (flags & XFRM_SPD_HMAX)
NLA_PUT_U32(skb, XFRMA_SPDHMAX, si.spdhmcnt);
if (flags & XFRM_SPD_ICNT)
NLA_PUT_U32(skb, XFRMA_SPDICNT, si.incnt);
if (flags & XFRM_SPD_OCNT)
NLA_PUT_U32(skb, XFRMA_SPDOCNT, si.outcnt);
if (flags & XFRM_SPD_FCNT)
NLA_PUT_U32(skb, XFRMA_SPDFCNT, si.fwdcnt);
if (flags & XFRM_SPD_ISCNT)
NLA_PUT_U32(skb, XFRMA_SPDISCNT, si.inscnt);
if (flags & XFRM_SPD_OSCNT)
NLA_PUT_U32(skb, XFRMA_SPDOSCNT, si.inscnt);
if (flags & XFRM_SPD_FSCNT)
NLA_PUT_U32(skb, XFRMA_SPDFSCNT, si.inscnt);
return nlmsg_end(skb, nlh);
nla_put_failure:
nlmsg_cancel(skb, nlh);
return -EMSGSIZE;
}
static int xfrm_get_spdinfo(struct sk_buff *skb, struct nlmsghdr *nlh,
struct rtattr **xfrma)
{
struct sk_buff *r_skb;
u32 *flags = NLMSG_DATA(nlh);
u32 spid = NETLINK_CB(skb).pid;
u32 seq = nlh->nlmsg_seq;
int len = NLMSG_LENGTH(sizeof(u32));
if (*flags & XFRM_SPD_HMASK)
len += RTA_SPACE(sizeof(u32));
if (*flags & XFRM_SPD_HMAX)
len += RTA_SPACE(sizeof(u32));
if (*flags & XFRM_SPD_ICNT)
len += RTA_SPACE(sizeof(u32));
if (*flags & XFRM_SPD_OCNT)
len += RTA_SPACE(sizeof(u32));
if (*flags & XFRM_SPD_FCNT)
len += RTA_SPACE(sizeof(u32));
if (*flags & XFRM_SPD_ISCNT)
len += RTA_SPACE(sizeof(u32));
if (*flags & XFRM_SPD_OSCNT)
len += RTA_SPACE(sizeof(u32));
if (*flags & XFRM_SPD_FSCNT)
len += RTA_SPACE(sizeof(u32));
r_skb = alloc_skb(len, GFP_ATOMIC);
if (r_skb == NULL)
return -ENOMEM;
if (build_spdinfo(r_skb, spid, seq, *flags) < 0)
BUG();
return nlmsg_unicast(xfrm_nl, r_skb, spid);
}
static int build_sadinfo(struct sk_buff *skb, u32 pid, u32 seq, u32 flags)
{
struct xfrm_sadinfo si;
......@@ -1879,6 +1954,7 @@ static const int xfrm_msg_min[XFRM_NR_MSGTYPES] = {
[XFRM_MSG_REPORT - XFRM_MSG_BASE] = XMSGSIZE(xfrm_user_report),
[XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = XMSGSIZE(xfrm_userpolicy_id),
[XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = NLMSG_LENGTH(sizeof(u32)),
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = NLMSG_LENGTH(sizeof(u32)),
};
#undef XMSGSIZE
......@@ -1907,6 +1983,7 @@ static struct xfrm_link {
[XFRM_MSG_GETAE - XFRM_MSG_BASE] = { .doit = xfrm_get_ae },
[XFRM_MSG_MIGRATE - XFRM_MSG_BASE] = { .doit = xfrm_do_migrate },
[XFRM_MSG_GETSADINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_sadinfo },
[XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo },
};
static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册