Merge tag 'v3.10.106' into update
This is the 3.10.106 stable release
This commit is contained in:
+10
-2
@@ -425,6 +425,7 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
|
||||
* @func: callback function on filter match
|
||||
* @data: returned parameter for callback function
|
||||
* @ident: string for calling module indentification
|
||||
* @sk: socket pointer (might be NULL)
|
||||
*
|
||||
* Description:
|
||||
* Invokes the callback function with the received sk_buff and the given
|
||||
@@ -448,7 +449,7 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
|
||||
*/
|
||||
int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
|
||||
void (*func)(struct sk_buff *, void *), void *data,
|
||||
char *ident)
|
||||
char *ident, struct sock *sk)
|
||||
{
|
||||
struct receiver *r;
|
||||
struct hlist_head *rl;
|
||||
@@ -476,6 +477,7 @@ int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
|
||||
r->func = func;
|
||||
r->data = data;
|
||||
r->ident = ident;
|
||||
r->sk = sk;
|
||||
|
||||
hlist_add_head_rcu(&r->list, rl);
|
||||
d->entries++;
|
||||
@@ -500,8 +502,11 @@ EXPORT_SYMBOL(can_rx_register);
|
||||
static void can_rx_delete_receiver(struct rcu_head *rp)
|
||||
{
|
||||
struct receiver *r = container_of(rp, struct receiver, rcu);
|
||||
struct sock *sk = r->sk;
|
||||
|
||||
kmem_cache_free(rcv_cache, r);
|
||||
if (sk)
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -576,8 +581,11 @@ void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
|
||||
spin_unlock(&can_rcvlists_lock);
|
||||
|
||||
/* schedule the receiver item for deletion */
|
||||
if (r)
|
||||
if (r) {
|
||||
if (r->sk)
|
||||
sock_hold(r->sk);
|
||||
call_rcu(&r->rcu, can_rx_delete_receiver);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(can_rx_unregister);
|
||||
|
||||
|
||||
+2
-1
@@ -50,13 +50,14 @@
|
||||
|
||||
struct receiver {
|
||||
struct hlist_node list;
|
||||
struct rcu_head rcu;
|
||||
canid_t can_id;
|
||||
canid_t mask;
|
||||
unsigned long matches;
|
||||
void (*func)(struct sk_buff *, void *);
|
||||
void *data;
|
||||
char *ident;
|
||||
struct sock *sk;
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
|
||||
|
||||
+2
-2
@@ -1169,7 +1169,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
|
||||
err = can_rx_register(dev, op->can_id,
|
||||
REGMASK(op->can_id),
|
||||
bcm_rx_handler, op,
|
||||
"bcm");
|
||||
"bcm", sk);
|
||||
|
||||
op->rx_reg_dev = dev;
|
||||
dev_put(dev);
|
||||
@@ -1178,7 +1178,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
|
||||
} else
|
||||
err = can_rx_register(NULL, op->can_id,
|
||||
REGMASK(op->can_id),
|
||||
bcm_rx_handler, op, "bcm");
|
||||
bcm_rx_handler, op, "bcm", sk);
|
||||
if (err) {
|
||||
/* this bcm rx op is broken -> remove it */
|
||||
list_del(&op->list);
|
||||
|
||||
+1
-1
@@ -435,7 +435,7 @@ static inline int cgw_register_filter(struct cgw_job *gwj)
|
||||
{
|
||||
return can_rx_register(gwj->src.dev, gwj->ccgw.filter.can_id,
|
||||
gwj->ccgw.filter.can_mask, can_can_gw_rcv,
|
||||
gwj, "gw");
|
||||
gwj, "gw", NULL);
|
||||
}
|
||||
|
||||
static inline void cgw_unregister_filter(struct cgw_job *gwj)
|
||||
|
||||
+2
-2
@@ -168,7 +168,7 @@ static int raw_enable_filters(struct net_device *dev, struct sock *sk,
|
||||
for (i = 0; i < count; i++) {
|
||||
err = can_rx_register(dev, filter[i].can_id,
|
||||
filter[i].can_mask,
|
||||
raw_rcv, sk, "raw");
|
||||
raw_rcv, sk, "raw", sk);
|
||||
if (err) {
|
||||
/* clean up successfully registered filters */
|
||||
while (--i >= 0)
|
||||
@@ -189,7 +189,7 @@ static int raw_enable_errfilter(struct net_device *dev, struct sock *sk,
|
||||
|
||||
if (err_mask)
|
||||
err = can_rx_register(dev, 0, err_mask | CAN_ERR_FLAG,
|
||||
raw_rcv, sk, "raw");
|
||||
raw_rcv, sk, "raw", sk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -870,7 +870,6 @@ static int decode_new_up_state_weight(void **p, void *end,
|
||||
if ((map->osd_state[osd] & CEPH_OSD_EXISTS) &&
|
||||
(xorstate & CEPH_OSD_EXISTS)) {
|
||||
pr_info("osd%d does not exist\n", osd);
|
||||
map->osd_weight[osd] = CEPH_OSD_IN;
|
||||
memset(map->osd_addr + osd, 0, sizeof(*map->osd_addr));
|
||||
map->osd_state[osd] = 0;
|
||||
} else {
|
||||
|
||||
+41
-17
@@ -1563,37 +1563,59 @@ EXPORT_SYMBOL(call_netdevice_notifiers);
|
||||
|
||||
static struct static_key netstamp_needed __read_mostly;
|
||||
#ifdef HAVE_JUMP_LABEL
|
||||
/* We are not allowed to call static_key_slow_dec() from irq context
|
||||
* If net_disable_timestamp() is called from irq context, defer the
|
||||
* static_key_slow_dec() calls.
|
||||
*/
|
||||
static atomic_t netstamp_needed_deferred;
|
||||
static atomic_t netstamp_wanted;
|
||||
static void netstamp_clear(struct work_struct *work)
|
||||
{
|
||||
int deferred = atomic_xchg(&netstamp_needed_deferred, 0);
|
||||
int wanted;
|
||||
|
||||
wanted = atomic_add_return(deferred, &netstamp_wanted);
|
||||
if (wanted > 0)
|
||||
static_key_enable(&netstamp_needed);
|
||||
else
|
||||
static_key_disable(&netstamp_needed);
|
||||
}
|
||||
static DECLARE_WORK(netstamp_work, netstamp_clear);
|
||||
#endif
|
||||
|
||||
void net_enable_timestamp(void)
|
||||
{
|
||||
#ifdef HAVE_JUMP_LABEL
|
||||
int deferred = atomic_xchg(&netstamp_needed_deferred, 0);
|
||||
int wanted;
|
||||
|
||||
if (deferred) {
|
||||
while (--deferred)
|
||||
static_key_slow_dec(&netstamp_needed);
|
||||
return;
|
||||
while (1) {
|
||||
wanted = atomic_read(&netstamp_wanted);
|
||||
if (wanted <= 0)
|
||||
break;
|
||||
if (atomic_cmpxchg(&netstamp_wanted, wanted, wanted + 1) == wanted)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
atomic_inc(&netstamp_needed_deferred);
|
||||
schedule_work(&netstamp_work);
|
||||
#else
|
||||
static_key_slow_inc(&netstamp_needed);
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(net_enable_timestamp);
|
||||
|
||||
void net_disable_timestamp(void)
|
||||
{
|
||||
#ifdef HAVE_JUMP_LABEL
|
||||
if (in_interrupt()) {
|
||||
atomic_inc(&netstamp_needed_deferred);
|
||||
return;
|
||||
int wanted;
|
||||
|
||||
while (1) {
|
||||
wanted = atomic_read(&netstamp_wanted);
|
||||
if (wanted <= 1)
|
||||
break;
|
||||
if (atomic_cmpxchg(&netstamp_wanted, wanted, wanted - 1) == wanted)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
atomic_dec(&netstamp_needed_deferred);
|
||||
schedule_work(&netstamp_work);
|
||||
#else
|
||||
static_key_slow_dec(&netstamp_needed);
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(net_disable_timestamp);
|
||||
|
||||
@@ -2465,9 +2487,9 @@ static netdev_features_t harmonize_features(struct sk_buff *skb,
|
||||
if (skb->ip_summed != CHECKSUM_NONE &&
|
||||
!can_checksum_protocol(features, protocol)) {
|
||||
features &= ~NETIF_F_ALL_CSUM;
|
||||
} else if (illegal_highdma(dev, skb)) {
|
||||
features &= ~NETIF_F_SG;
|
||||
}
|
||||
if (illegal_highdma(dev, skb))
|
||||
features &= ~NETIF_F_SG;
|
||||
|
||||
return features;
|
||||
}
|
||||
@@ -3912,7 +3934,9 @@ static void skb_gro_reset_offset(struct sk_buff *skb)
|
||||
pinfo->nr_frags &&
|
||||
!PageHighMem(skb_frag_page(frag0))) {
|
||||
NAPI_GRO_CB(skb)->frag0 = skb_frag_address(frag0);
|
||||
NAPI_GRO_CB(skb)->frag0_len = skb_frag_size(frag0);
|
||||
NAPI_GRO_CB(skb)->frag0_len = min_t(unsigned int,
|
||||
skb_frag_size(frag0),
|
||||
skb->end - skb->tail);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -1407,6 +1407,11 @@ static void __sk_free(struct sock *sk)
|
||||
pr_debug("%s: optmem leakage (%d bytes) detected\n",
|
||||
__func__, atomic_read(&sk->sk_omem_alloc));
|
||||
|
||||
if (sk->sk_frag.page) {
|
||||
put_page(sk->sk_frag.page);
|
||||
sk->sk_frag.page = NULL;
|
||||
}
|
||||
|
||||
if (sk->sk_peer_cred)
|
||||
put_cred(sk->sk_peer_cred);
|
||||
put_pid(sk->sk_peer_pid);
|
||||
@@ -2681,11 +2686,6 @@ void sk_common_release(struct sock *sk)
|
||||
|
||||
sk_refcnt_debug_release(sk);
|
||||
|
||||
if (sk->sk_frag.page) {
|
||||
put_page(sk->sk_frag.page);
|
||||
sk->sk_frag.page = NULL;
|
||||
}
|
||||
|
||||
sock_put(sk);
|
||||
}
|
||||
EXPORT_SYMBOL(sk_common_release);
|
||||
|
||||
+2
-1
@@ -263,7 +263,8 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info)
|
||||
|
||||
switch (type) {
|
||||
case ICMP_REDIRECT:
|
||||
dccp_do_redirect(skb, sk);
|
||||
if (!sock_owned_by_user(sk))
|
||||
dccp_do_redirect(skb, sk);
|
||||
goto out;
|
||||
case ICMP_SOURCE_QUENCH:
|
||||
/* Just silently ignore these. */
|
||||
|
||||
+5
-3
@@ -132,10 +132,12 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
||||
np = inet6_sk(sk);
|
||||
|
||||
if (type == NDISC_REDIRECT) {
|
||||
struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
|
||||
if (!sock_owned_by_user(sk)) {
|
||||
struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
|
||||
|
||||
if (dst)
|
||||
dst->ops->redirect(dst, sk, skb);
|
||||
if (dst)
|
||||
dst->ops->redirect(dst, sk, skb);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
@@ -459,7 +459,7 @@ static int lowpan_header_create(struct sk_buff *skb,
|
||||
hc06_ptr += 3;
|
||||
} else {
|
||||
/* compress nothing */
|
||||
memcpy(hc06_ptr, &hdr, 4);
|
||||
memcpy(hc06_ptr, hdr, 4);
|
||||
/* replace the top byte with new ECN | DSCP format */
|
||||
*hc06_ptr = tmp;
|
||||
hc06_ptr += 4;
|
||||
|
||||
@@ -1649,6 +1649,10 @@ int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option)
|
||||
goto validate_return_locked;
|
||||
}
|
||||
|
||||
if (opt_iter + 1 == opt_len) {
|
||||
err_offset = opt_iter;
|
||||
goto validate_return_locked;
|
||||
}
|
||||
tag_len = tag[1];
|
||||
if (tag_len > (opt_len - opt_iter)) {
|
||||
err_offset = opt_iter + 1;
|
||||
|
||||
+4
-2
@@ -1874,7 +1874,7 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
|
||||
|
||||
rtnl_lock();
|
||||
in_dev = ip_mc_find_dev(net, imr);
|
||||
if (!in_dev) {
|
||||
if (!imr->imr_ifindex && !imr->imr_address.s_addr && !in_dev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
@@ -1895,8 +1895,10 @@ int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr)
|
||||
|
||||
*imlp = iml->next_rcu;
|
||||
|
||||
ip_mc_dec_group(in_dev, group);
|
||||
if (in_dev)
|
||||
ip_mc_dec_group(in_dev, group);
|
||||
rtnl_unlock();
|
||||
|
||||
/* decrease mem now to avoid the memleak warning */
|
||||
atomic_sub(sizeof(*iml), &sk->sk_omem_alloc);
|
||||
kfree_rcu(iml, rcu);
|
||||
|
||||
@@ -690,7 +690,7 @@ struct sock *inet_csk_clone_lock(const struct sock *sk,
|
||||
inet_sk(newsk)->inet_sport = inet_rsk(req)->loc_port;
|
||||
newsk->sk_write_space = sk_stream_write_space;
|
||||
|
||||
newsk->sk_mark = inet_rsk(req)->ir_mark;
|
||||
inet_sk(newsk)->mc_list = NULL;
|
||||
|
||||
newicsk->icsk_retransmits = 0;
|
||||
newicsk->icsk_backoff = 0;
|
||||
|
||||
@@ -1049,7 +1049,7 @@ void ipv4_pktinfo_prepare(struct sk_buff *skb)
|
||||
if (unlikely(IPCB(skb)->opt.optlen))
|
||||
skb_dst_force(skb);
|
||||
else
|
||||
skb_dst_drop(skb);
|
||||
skb_dst_drop(skb);
|
||||
}
|
||||
|
||||
int ip_setsockopt(struct sock *sk, int level,
|
||||
|
||||
@@ -582,7 +582,6 @@ static void vti_tunnel_setup(struct net_device *dev)
|
||||
dev->type = ARPHRD_TUNNEL;
|
||||
dev->destructor = vti_dev_free;
|
||||
|
||||
dev->hard_header_len = LL_MAX_HEADER + sizeof(struct iphdr);
|
||||
dev->mtu = ETH_DATA_LEN;
|
||||
dev->flags = IFF_NOARP;
|
||||
dev->iflink = 0;
|
||||
|
||||
@@ -1321,8 +1321,8 @@ static int translate_compat_table(struct xt_table_info **pinfo,
|
||||
|
||||
newinfo->number = compatr->num_entries;
|
||||
for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
|
||||
newinfo->hook_entry[i] = info->hook_entry[i];
|
||||
newinfo->underflow[i] = info->underflow[i];
|
||||
newinfo->hook_entry[i] = compatr->hook_entry[i];
|
||||
newinfo->underflow[i] = compatr->underflow[i];
|
||||
}
|
||||
entry1 = newinfo->entries[raw_smp_processor_id()];
|
||||
pos = entry1;
|
||||
|
||||
+5
-4
@@ -150,18 +150,17 @@ void ping_hash(struct sock *sk)
|
||||
void ping_unhash(struct sock *sk)
|
||||
{
|
||||
struct inet_sock *isk = inet_sk(sk);
|
||||
pr_info("ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num);
|
||||
pr_debug("ping_v4_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num);
|
||||
write_lock_bh(&ping_table.lock);
|
||||
if (sk_hashed(sk)) {
|
||||
write_lock_bh(&ping_table.lock);
|
||||
hlist_nulls_del(&sk->sk_nulls_node);
|
||||
sk_nulls_node_init(&sk->sk_nulls_node);
|
||||
sock_put(sk);
|
||||
isk->inet_num = 0;
|
||||
isk->inet_sport = 0;
|
||||
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
|
||||
write_unlock_bh(&ping_table.lock);
|
||||
pr_info("ping_unhash(isk=%p,sk=%p)\n", isk, sk);
|
||||
}
|
||||
write_unlock_bh(&ping_table.lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ping_unhash);
|
||||
|
||||
@@ -628,6 +627,8 @@ static int ping_v4_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh,
|
||||
{
|
||||
struct sk_buff *skb = skb_peek(&sk->sk_write_queue);
|
||||
|
||||
if (!skb)
|
||||
return 0;
|
||||
pfh->wcheck = csum_partial((char *)&pfh->icmph,
|
||||
sizeof(struct icmphdr), pfh->wcheck);
|
||||
pfh->icmph.checksum = csum_fold(pfh->wcheck);
|
||||
|
||||
@@ -1798,6 +1798,7 @@ int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr,
|
||||
{
|
||||
int res;
|
||||
|
||||
tos &= IPTOS_RT_MASK;
|
||||
rcu_read_lock();
|
||||
|
||||
/* Multicast recognition logic is moved from route cache to here.
|
||||
|
||||
@@ -747,6 +747,12 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
/* if __tcp_splice_read() got nothing while we have
|
||||
* an skb in receive queue, we do not want to loop.
|
||||
* This might happen with URG data.
|
||||
*/
|
||||
if (!skb_queue_empty(&sk->sk_receive_queue))
|
||||
break;
|
||||
sk_wait_data(sk, &timeo);
|
||||
if (signal_pending(current)) {
|
||||
ret = sock_intr_errno(timeo);
|
||||
|
||||
+3
-1
@@ -389,7 +389,8 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
|
||||
|
||||
switch (type) {
|
||||
case ICMP_REDIRECT:
|
||||
do_redirect(icmp_skb, sk);
|
||||
if (!sock_owned_by_user(sk))
|
||||
do_redirect(icmp_skb, sk);
|
||||
goto out;
|
||||
case ICMP_SOURCE_QUENCH:
|
||||
/* Just silently ignore these. */
|
||||
@@ -1422,6 +1423,7 @@ static int tcp_v4_conn_req_fastopen(struct sock *sk,
|
||||
* scaled. So correct it appropriately.
|
||||
*/
|
||||
tp->snd_wnd = ntohs(tcp_hdr(skb)->window);
|
||||
tp->max_window = tp->snd_wnd;
|
||||
|
||||
/* Activate the retrans timer so that SYNACK can be retransmitted.
|
||||
* The request socket is not added to the SYN table of the parent
|
||||
|
||||
+22
-19
@@ -55,6 +55,7 @@
|
||||
#include <net/ip6_fib.h>
|
||||
#include <net/ip6_route.h>
|
||||
#include <net/ip6_tunnel.h>
|
||||
#include <net/gre.h>
|
||||
|
||||
|
||||
static bool log_ecn_error = true;
|
||||
@@ -365,35 +366,37 @@ static void ip6gre_tunnel_uninit(struct net_device *dev)
|
||||
|
||||
|
||||
static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
||||
u8 type, u8 code, int offset, __be32 info)
|
||||
u8 type, u8 code, int offset, __be32 info)
|
||||
{
|
||||
const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb->data;
|
||||
__be16 *p = (__be16 *)(skb->data + offset);
|
||||
int grehlen = offset + 4;
|
||||
const struct gre_base_hdr *greh;
|
||||
const struct ipv6hdr *ipv6h;
|
||||
int grehlen = sizeof(*greh);
|
||||
struct ip6_tnl *t;
|
||||
int key_off = 0;
|
||||
__be16 flags;
|
||||
__be32 key;
|
||||
|
||||
flags = p[0];
|
||||
if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
|
||||
if (flags&(GRE_VERSION|GRE_ROUTING))
|
||||
return;
|
||||
if (flags&GRE_KEY) {
|
||||
grehlen += 4;
|
||||
if (flags&GRE_CSUM)
|
||||
grehlen += 4;
|
||||
}
|
||||
if (!pskb_may_pull(skb, offset + grehlen))
|
||||
return;
|
||||
greh = (const struct gre_base_hdr *)(skb->data + offset);
|
||||
flags = greh->flags;
|
||||
if (flags & (GRE_VERSION | GRE_ROUTING))
|
||||
return;
|
||||
if (flags & GRE_CSUM)
|
||||
grehlen += 4;
|
||||
if (flags & GRE_KEY) {
|
||||
key_off = grehlen + offset;
|
||||
grehlen += 4;
|
||||
}
|
||||
|
||||
/* If only 8 bytes returned, keyed message will be dropped here */
|
||||
if (!pskb_may_pull(skb, grehlen))
|
||||
if (!pskb_may_pull(skb, offset + grehlen))
|
||||
return;
|
||||
ipv6h = (const struct ipv6hdr *)skb->data;
|
||||
p = (__be16 *)(skb->data + offset);
|
||||
greh = (const struct gre_base_hdr *)(skb->data + offset);
|
||||
key = key_off ? *(__be32 *)(skb->data + key_off) : 0;
|
||||
|
||||
t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr,
|
||||
flags & GRE_KEY ?
|
||||
*(((__be32 *)p) + (grehlen / 4) - 1) : 0,
|
||||
p[1]);
|
||||
key, greh->protocol);
|
||||
if (t == NULL)
|
||||
return;
|
||||
|
||||
|
||||
@@ -174,6 +174,7 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
|
||||
ops = rcu_dereference(inet6_offloads[proto]);
|
||||
if (!ops || !ops->callbacks.gro_receive) {
|
||||
__pskb_pull(skb, skb_gro_offset(skb));
|
||||
skb_gro_frag0_invalidate(skb);
|
||||
proto = ipv6_gso_pull_exthdrs(skb, proto);
|
||||
skb_gro_pull(skb, -skb_transport_offset(skb));
|
||||
skb_reset_transport_header(skb);
|
||||
|
||||
+38
-17
@@ -103,16 +103,25 @@ struct ip6_tnl_net {
|
||||
|
||||
static struct net_device_stats *ip6_get_stats(struct net_device *dev)
|
||||
{
|
||||
struct pcpu_tstats sum = { 0 };
|
||||
struct pcpu_tstats tmp, sum = { 0 };
|
||||
int i;
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
unsigned int start;
|
||||
const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i);
|
||||
|
||||
sum.rx_packets += tstats->rx_packets;
|
||||
sum.rx_bytes += tstats->rx_bytes;
|
||||
sum.tx_packets += tstats->tx_packets;
|
||||
sum.tx_bytes += tstats->tx_bytes;
|
||||
do {
|
||||
start = u64_stats_fetch_begin_bh(&tstats->syncp);
|
||||
tmp.rx_packets = tstats->rx_packets;
|
||||
tmp.rx_bytes = tstats->rx_bytes;
|
||||
tmp.tx_packets = tstats->tx_packets;
|
||||
tmp.tx_bytes = tstats->tx_bytes;
|
||||
} while (u64_stats_fetch_retry_bh(&tstats->syncp, start));
|
||||
|
||||
sum.rx_packets += tmp.rx_packets;
|
||||
sum.rx_bytes += tmp.rx_bytes;
|
||||
sum.tx_packets += tmp.tx_packets;
|
||||
sum.tx_bytes += tmp.tx_bytes;
|
||||
}
|
||||
dev->stats.rx_packets = sum.rx_packets;
|
||||
dev->stats.rx_bytes = sum.rx_bytes;
|
||||
@@ -394,18 +403,19 @@ ip6_tnl_dev_uninit(struct net_device *dev)
|
||||
|
||||
__u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw)
|
||||
{
|
||||
const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) raw;
|
||||
__u8 nexthdr = ipv6h->nexthdr;
|
||||
__u16 off = sizeof (*ipv6h);
|
||||
const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)raw;
|
||||
unsigned int nhoff = raw - skb->data;
|
||||
unsigned int off = nhoff + sizeof(*ipv6h);
|
||||
u8 next, nexthdr = ipv6h->nexthdr;
|
||||
|
||||
while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) {
|
||||
__u16 optlen = 0;
|
||||
struct ipv6_opt_hdr *hdr;
|
||||
if (raw + off + sizeof (*hdr) > skb->data &&
|
||||
!pskb_may_pull(skb, raw - skb->data + off + sizeof (*hdr)))
|
||||
u16 optlen;
|
||||
|
||||
if (!pskb_may_pull(skb, off + sizeof(*hdr)))
|
||||
break;
|
||||
|
||||
hdr = (struct ipv6_opt_hdr *) (raw + off);
|
||||
hdr = (struct ipv6_opt_hdr *)(skb->data + off);
|
||||
if (nexthdr == NEXTHDR_FRAGMENT) {
|
||||
struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr;
|
||||
if (frag_hdr->frag_off)
|
||||
@@ -416,20 +426,29 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw)
|
||||
} else {
|
||||
optlen = ipv6_optlen(hdr);
|
||||
}
|
||||
/* cache hdr->nexthdr, since pskb_may_pull() might
|
||||
* invalidate hdr
|
||||
*/
|
||||
next = hdr->nexthdr;
|
||||
if (nexthdr == NEXTHDR_DEST) {
|
||||
__u16 i = off + 2;
|
||||
u16 i = 2;
|
||||
|
||||
/* Remember : hdr is no longer valid at this point. */
|
||||
if (!pskb_may_pull(skb, off + optlen))
|
||||
break;
|
||||
|
||||
while (1) {
|
||||
struct ipv6_tlv_tnl_enc_lim *tel;
|
||||
|
||||
/* No more room for encapsulation limit */
|
||||
if (i + sizeof (*tel) > off + optlen)
|
||||
if (i + sizeof(*tel) > optlen)
|
||||
break;
|
||||
|
||||
tel = (struct ipv6_tlv_tnl_enc_lim *) &raw[i];
|
||||
tel = (struct ipv6_tlv_tnl_enc_lim *)(skb->data + off + i);
|
||||
/* return index of option if found and valid */
|
||||
if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT &&
|
||||
tel->length == 1)
|
||||
return i;
|
||||
return i + off - nhoff;
|
||||
/* else jump to next option */
|
||||
if (tel->type)
|
||||
i += tel->length + 2;
|
||||
@@ -437,7 +456,7 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
nexthdr = hdr->nexthdr;
|
||||
nexthdr = next;
|
||||
off += optlen;
|
||||
}
|
||||
return 0;
|
||||
@@ -822,8 +841,10 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol,
|
||||
}
|
||||
|
||||
tstats = this_cpu_ptr(t->dev->tstats);
|
||||
u64_stats_update_begin(&tstats->syncp);
|
||||
tstats->rx_packets++;
|
||||
tstats->rx_bytes += skb->len;
|
||||
u64_stats_update_end(&tstats->syncp);
|
||||
|
||||
netif_rx(skb);
|
||||
|
||||
|
||||
+5
-2
@@ -578,8 +578,11 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
|
||||
}
|
||||
|
||||
offset += skb_transport_offset(skb);
|
||||
if (skb_copy_bits(skb, offset, &csum, 2))
|
||||
BUG();
|
||||
err = skb_copy_bits(skb, offset, &csum, 2);
|
||||
if (err < 0) {
|
||||
ip6_flush_pending_frames(sk);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* in case cksum was not initialized */
|
||||
if (unlikely(csum))
|
||||
|
||||
+5
-3
@@ -384,10 +384,12 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
||||
np = inet6_sk(sk);
|
||||
|
||||
if (type == NDISC_REDIRECT) {
|
||||
struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
|
||||
if (!sock_owned_by_user(sk)) {
|
||||
struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
|
||||
|
||||
if (dst)
|
||||
dst->ops->redirect(dst, sk, skb);
|
||||
if (dst)
|
||||
dst->ops->redirect(dst, sk, skb);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
@@ -280,7 +280,8 @@ struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunn
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l2tp_session_find);
|
||||
|
||||
struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth)
|
||||
struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth,
|
||||
bool do_ref)
|
||||
{
|
||||
int hash;
|
||||
struct l2tp_session *session;
|
||||
@@ -290,6 +291,9 @@ struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth)
|
||||
for (hash = 0; hash < L2TP_HASH_SIZE; hash++) {
|
||||
hlist_for_each_entry(session, &tunnel->session_hlist[hash], hlist) {
|
||||
if (++count > nth) {
|
||||
l2tp_session_inc_refcount(session);
|
||||
if (do_ref && session->ref)
|
||||
session->ref(session);
|
||||
read_unlock_bh(&tunnel->hlist_lock);
|
||||
return session;
|
||||
}
|
||||
@@ -300,7 +304,7 @@ struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(l2tp_session_find_nth);
|
||||
EXPORT_SYMBOL_GPL(l2tp_session_get_nth);
|
||||
|
||||
/* Lookup a session by interface name.
|
||||
* This is very inefficient but is only used by management interfaces.
|
||||
|
||||
@@ -236,7 +236,8 @@ out:
|
||||
extern struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel);
|
||||
extern void l2tp_tunnel_sock_put(struct sock *sk);
|
||||
extern struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id);
|
||||
extern struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth);
|
||||
extern struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth,
|
||||
bool do_ref);
|
||||
extern struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname);
|
||||
extern struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id);
|
||||
extern struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth);
|
||||
@@ -256,6 +257,7 @@ extern int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int
|
||||
|
||||
extern int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops);
|
||||
extern void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type);
|
||||
int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg);
|
||||
|
||||
/* Session reference counts. Incremented when code obtains a reference
|
||||
* to a session.
|
||||
|
||||
@@ -53,7 +53,7 @@ static void l2tp_dfs_next_tunnel(struct l2tp_dfs_seq_data *pd)
|
||||
|
||||
static void l2tp_dfs_next_session(struct l2tp_dfs_seq_data *pd)
|
||||
{
|
||||
pd->session = l2tp_session_find_nth(pd->tunnel, pd->session_idx);
|
||||
pd->session = l2tp_session_get_nth(pd->tunnel, pd->session_idx, true);
|
||||
pd->session_idx++;
|
||||
|
||||
if (pd->session == NULL) {
|
||||
@@ -237,10 +237,14 @@ static int l2tp_dfs_seq_show(struct seq_file *m, void *v)
|
||||
}
|
||||
|
||||
/* Show the tunnel or session context */
|
||||
if (pd->session == NULL)
|
||||
if (!pd->session) {
|
||||
l2tp_dfs_seq_tunnel_show(m, pd->tunnel);
|
||||
else
|
||||
} else {
|
||||
l2tp_dfs_seq_session_show(m, pd->session);
|
||||
if (pd->session->deref)
|
||||
pd->session->deref(pd->session);
|
||||
l2tp_session_dec_refcount(pd->session);
|
||||
}
|
||||
|
||||
out:
|
||||
return 0;
|
||||
|
||||
+26
-1
@@ -11,6 +11,7 @@
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <asm/ioctls.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
@@ -555,6 +556,30 @@ out:
|
||||
return err ? err : copied;
|
||||
}
|
||||
|
||||
int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int amount;
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCOUTQ:
|
||||
amount = sk_wmem_alloc_get(sk);
|
||||
break;
|
||||
case SIOCINQ:
|
||||
spin_lock_bh(&sk->sk_receive_queue.lock);
|
||||
skb = skb_peek(&sk->sk_receive_queue);
|
||||
amount = skb ? skb->len : 0;
|
||||
spin_unlock_bh(&sk->sk_receive_queue.lock);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
return put_user(amount, (int __user *)arg);
|
||||
}
|
||||
EXPORT_SYMBOL(l2tp_ioctl);
|
||||
|
||||
static struct proto l2tp_ip_prot = {
|
||||
.name = "L2TP/IP",
|
||||
.owner = THIS_MODULE,
|
||||
@@ -563,7 +588,7 @@ static struct proto l2tp_ip_prot = {
|
||||
.bind = l2tp_ip_bind,
|
||||
.connect = l2tp_ip_connect,
|
||||
.disconnect = l2tp_ip_disconnect,
|
||||
.ioctl = udp_ioctl,
|
||||
.ioctl = l2tp_ioctl,
|
||||
.destroy = l2tp_ip_destroy_sock,
|
||||
.setsockopt = ip_setsockopt,
|
||||
.getsockopt = ip_getsockopt,
|
||||
|
||||
+1
-1
@@ -717,7 +717,7 @@ static struct proto l2tp_ip6_prot = {
|
||||
.bind = l2tp_ip6_bind,
|
||||
.connect = l2tp_ip6_connect,
|
||||
.disconnect = l2tp_ip6_disconnect,
|
||||
.ioctl = udp_ioctl,
|
||||
.ioctl = l2tp_ioctl,
|
||||
.destroy = l2tp_ip6_destroy_sock,
|
||||
.setsockopt = ipv6_setsockopt,
|
||||
.getsockopt = ipv6_getsockopt,
|
||||
|
||||
@@ -719,7 +719,7 @@ static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback
|
||||
goto out;
|
||||
}
|
||||
|
||||
session = l2tp_session_find_nth(tunnel, si);
|
||||
session = l2tp_session_get_nth(tunnel, si, false);
|
||||
if (session == NULL) {
|
||||
ti++;
|
||||
tunnel = NULL;
|
||||
@@ -729,8 +729,11 @@ static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback
|
||||
|
||||
if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||
session) <= 0)
|
||||
session) <= 0) {
|
||||
l2tp_session_dec_refcount(session);
|
||||
break;
|
||||
}
|
||||
l2tp_session_dec_refcount(session);
|
||||
|
||||
si++;
|
||||
}
|
||||
|
||||
+7
-3
@@ -1576,7 +1576,7 @@ static void pppol2tp_next_tunnel(struct net *net, struct pppol2tp_seq_data *pd)
|
||||
|
||||
static void pppol2tp_next_session(struct net *net, struct pppol2tp_seq_data *pd)
|
||||
{
|
||||
pd->session = l2tp_session_find_nth(pd->tunnel, pd->session_idx);
|
||||
pd->session = l2tp_session_get_nth(pd->tunnel, pd->session_idx, true);
|
||||
pd->session_idx++;
|
||||
|
||||
if (pd->session == NULL) {
|
||||
@@ -1703,10 +1703,14 @@ static int pppol2tp_seq_show(struct seq_file *m, void *v)
|
||||
|
||||
/* Show the tunnel or session context.
|
||||
*/
|
||||
if (pd->session == NULL)
|
||||
if (!pd->session) {
|
||||
pppol2tp_seq_tunnel_show(m, pd->tunnel);
|
||||
else
|
||||
} else {
|
||||
pppol2tp_seq_session_show(m, pd->session);
|
||||
if (pd->session->deref)
|
||||
pd->session->deref(pd->session);
|
||||
l2tp_session_dec_refcount(pd->session);
|
||||
}
|
||||
|
||||
out:
|
||||
return 0;
|
||||
|
||||
+1
-1
@@ -345,7 +345,7 @@ int mesh_add_vendor_ies(struct ieee80211_sub_if_data *sdata,
|
||||
/* fast-forward to vendor IEs */
|
||||
offset = ieee80211_ie_split_vendor(ifmsh->ie, ifmsh->ie_len, 0);
|
||||
|
||||
if (offset) {
|
||||
if (offset < ifmsh->ie_len) {
|
||||
len = ifmsh->ie_len - offset;
|
||||
data = ifmsh->ie + offset;
|
||||
if (skb_tailroom(skb) < len)
|
||||
|
||||
+34
-19
@@ -1257,6 +1257,8 @@ static void __fanout_link(struct sock *sk, struct packet_sock *po)
|
||||
f->arr[f->num_members] = sk;
|
||||
smp_wmb();
|
||||
f->num_members++;
|
||||
if (f->num_members == 1)
|
||||
dev_add_pack(&f->prot_hook);
|
||||
spin_unlock(&f->lock);
|
||||
}
|
||||
|
||||
@@ -1273,6 +1275,8 @@ static void __fanout_unlink(struct sock *sk, struct packet_sock *po)
|
||||
BUG_ON(i >= f->num_members);
|
||||
f->arr[i] = f->arr[f->num_members - 1];
|
||||
f->num_members--;
|
||||
if (f->num_members == 0)
|
||||
__dev_remove_pack(&f->prot_hook);
|
||||
spin_unlock(&f->lock);
|
||||
}
|
||||
|
||||
@@ -1304,13 +1308,16 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!po->running)
|
||||
return -EINVAL;
|
||||
|
||||
if (po->fanout)
|
||||
return -EALREADY;
|
||||
|
||||
mutex_lock(&fanout_mutex);
|
||||
|
||||
err = -EINVAL;
|
||||
if (!po->running)
|
||||
goto out;
|
||||
|
||||
err = -EALREADY;
|
||||
if (po->fanout)
|
||||
goto out;
|
||||
|
||||
match = NULL;
|
||||
list_for_each_entry(f, &fanout_list, list) {
|
||||
if (f->id == id &&
|
||||
@@ -1340,7 +1347,6 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
|
||||
match->prot_hook.func = packet_rcv_fanout;
|
||||
match->prot_hook.af_packet_priv = match;
|
||||
match->prot_hook.id_match = match_fanout_group;
|
||||
dev_add_pack(&match->prot_hook);
|
||||
list_add(&match->list, &fanout_list);
|
||||
}
|
||||
err = -EINVAL;
|
||||
@@ -1361,24 +1367,29 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void fanout_release(struct sock *sk)
|
||||
/* If pkt_sk(sk)->fanout->sk_ref is zero, this function removes
|
||||
* pkt_sk(sk)->fanout from fanout_list and returns pkt_sk(sk)->fanout.
|
||||
* It is the responsibility of the caller to call fanout_release_data() and
|
||||
* free the returned packet_fanout (after synchronize_net())
|
||||
*/
|
||||
static struct packet_fanout *fanout_release(struct sock *sk)
|
||||
{
|
||||
struct packet_sock *po = pkt_sk(sk);
|
||||
struct packet_fanout *f;
|
||||
|
||||
f = po->fanout;
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
mutex_lock(&fanout_mutex);
|
||||
po->fanout = NULL;
|
||||
f = po->fanout;
|
||||
if (f) {
|
||||
po->fanout = NULL;
|
||||
|
||||
if (atomic_dec_and_test(&f->sk_ref)) {
|
||||
list_del(&f->list);
|
||||
dev_remove_pack(&f->prot_hook);
|
||||
kfree(f);
|
||||
if (atomic_dec_and_test(&f->sk_ref))
|
||||
list_del(&f->list);
|
||||
else
|
||||
f = NULL;
|
||||
}
|
||||
mutex_unlock(&fanout_mutex);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
static const struct proto_ops packet_ops;
|
||||
@@ -2428,6 +2439,7 @@ static int packet_release(struct socket *sock)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct packet_sock *po;
|
||||
struct packet_fanout *f;
|
||||
struct net *net;
|
||||
union tpacket_req_u req_u;
|
||||
|
||||
@@ -2467,9 +2479,13 @@ static int packet_release(struct socket *sock)
|
||||
packet_set_ring(sk, &req_u, 1, 1);
|
||||
}
|
||||
|
||||
fanout_release(sk);
|
||||
f = fanout_release(sk);
|
||||
|
||||
synchronize_net();
|
||||
|
||||
if (f) {
|
||||
kfree(f);
|
||||
}
|
||||
/*
|
||||
* Now the socket is dead. No more input will appear.
|
||||
*/
|
||||
@@ -3392,7 +3408,6 @@ static int packet_notifier(struct notifier_block *this, unsigned long msg, void
|
||||
}
|
||||
if (msg == NETDEV_UNREGISTER) {
|
||||
packet_cached_dev_reset(po);
|
||||
fanout_release(sk);
|
||||
po->ifindex = -1;
|
||||
if (po->prot_hook.dev)
|
||||
dev_put(po->prot_hook.dev);
|
||||
|
||||
+1
-4
@@ -814,10 +814,8 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
|
||||
goto out_module_put;
|
||||
|
||||
err = a->ops->walk(skb, &dcb, RTM_DELACTION, a);
|
||||
if (err < 0)
|
||||
if (err <= 0)
|
||||
goto out_module_put;
|
||||
if (err == 0)
|
||||
goto noflush_out;
|
||||
|
||||
nla_nest_end(skb, nest);
|
||||
|
||||
@@ -835,7 +833,6 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
|
||||
out_module_put:
|
||||
module_put(a->ops->owner);
|
||||
err_out:
|
||||
noflush_out:
|
||||
kfree_skb(skb);
|
||||
kfree(a);
|
||||
return err;
|
||||
|
||||
+3
-1
@@ -136,12 +136,14 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
|
||||
unsigned long cl;
|
||||
unsigned long fh;
|
||||
int err;
|
||||
int tp_created = 0;
|
||||
int tp_created;
|
||||
|
||||
if ((n->nlmsg_type != RTM_GETTFILTER) && !netlink_capable(skb, CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
replay:
|
||||
tp_created = 0;
|
||||
|
||||
err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
+5
-4
@@ -176,11 +176,12 @@ META_COLLECTOR(int_vlan_tag)
|
||||
{
|
||||
unsigned short tag;
|
||||
|
||||
tag = vlan_tx_tag_get(skb);
|
||||
if (!tag && __vlan_get_tag(skb, &tag))
|
||||
*err = -1;
|
||||
else
|
||||
if (vlan_tx_tag_present(skb))
|
||||
dst->value = vlan_tx_tag_get(skb);
|
||||
else if (!__vlan_get_tag(skb, &tag))
|
||||
dst->value = tag;
|
||||
else
|
||||
*err = -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+93
-64
@@ -1301,82 +1301,111 @@ void sctp_assoc_update(struct sctp_association *asoc,
|
||||
}
|
||||
|
||||
/* Update the retran path for sending a retransmitted packet.
|
||||
* Round-robin through the active transports, else round-robin
|
||||
* through the inactive transports as this is the next best thing
|
||||
* we can try.
|
||||
* See also RFC4960, 6.4. Multi-Homed SCTP Endpoints:
|
||||
*
|
||||
* When there is outbound data to send and the primary path
|
||||
* becomes inactive (e.g., due to failures), or where the
|
||||
* SCTP user explicitly requests to send data to an
|
||||
* inactive destination transport address, before reporting
|
||||
* an error to its ULP, the SCTP endpoint should try to send
|
||||
* the data to an alternate active destination transport
|
||||
* address if one exists.
|
||||
*
|
||||
* When retransmitting data that timed out, if the endpoint
|
||||
* is multihomed, it should consider each source-destination
|
||||
* address pair in its retransmission selection policy.
|
||||
* When retransmitting timed-out data, the endpoint should
|
||||
* attempt to pick the most divergent source-destination
|
||||
* pair from the original source-destination pair to which
|
||||
* the packet was transmitted.
|
||||
*
|
||||
* Note: Rules for picking the most divergent source-destination
|
||||
* pair are an implementation decision and are not specified
|
||||
* within this document.
|
||||
*
|
||||
* Our basic strategy is to round-robin transports in priorities
|
||||
* according to sctp_state_prio_map[] e.g., if no such
|
||||
* transport with state SCTP_ACTIVE exists, round-robin through
|
||||
* SCTP_UNKNOWN, etc. You get the picture.
|
||||
*/
|
||||
void sctp_assoc_update_retran_path(struct sctp_association *asoc)
|
||||
static const u8 sctp_trans_state_to_prio_map[] = {
|
||||
[SCTP_ACTIVE] = 3, /* best case */
|
||||
[SCTP_UNKNOWN] = 2,
|
||||
[SCTP_PF] = 1,
|
||||
[SCTP_INACTIVE] = 0, /* worst case */
|
||||
};
|
||||
|
||||
static u8 sctp_trans_score(const struct sctp_transport *trans)
|
||||
{
|
||||
struct sctp_transport *t, *next;
|
||||
struct list_head *head = &asoc->peer.transport_addr_list;
|
||||
struct list_head *pos;
|
||||
|
||||
if (asoc->peer.transport_count == 1)
|
||||
return;
|
||||
|
||||
/* Find the next transport in a round-robin fashion. */
|
||||
t = asoc->peer.retran_path;
|
||||
pos = &t->transports;
|
||||
next = NULL;
|
||||
|
||||
while (1) {
|
||||
/* Skip the head. */
|
||||
if (pos->next == head)
|
||||
pos = head->next;
|
||||
else
|
||||
pos = pos->next;
|
||||
|
||||
t = list_entry(pos, struct sctp_transport, transports);
|
||||
|
||||
/* We have exhausted the list, but didn't find any
|
||||
* other active transports. If so, use the next
|
||||
* transport.
|
||||
*/
|
||||
if (t == asoc->peer.retran_path) {
|
||||
t = next;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Try to find an active transport. */
|
||||
|
||||
if ((t->state == SCTP_ACTIVE) ||
|
||||
(t->state == SCTP_UNKNOWN)) {
|
||||
break;
|
||||
} else {
|
||||
/* Keep track of the next transport in case
|
||||
* we don't find any active transport.
|
||||
*/
|
||||
if (t->state != SCTP_UNCONFIRMED && !next)
|
||||
next = t;
|
||||
}
|
||||
}
|
||||
|
||||
if (t)
|
||||
asoc->peer.retran_path = t;
|
||||
else
|
||||
t = asoc->peer.retran_path;
|
||||
|
||||
SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_update_retran_path:association"
|
||||
" %p addr: ",
|
||||
" port: %d\n",
|
||||
asoc,
|
||||
(&t->ipaddr),
|
||||
ntohs(t->ipaddr.v4.sin_port));
|
||||
return sctp_trans_state_to_prio_map[trans->state];
|
||||
}
|
||||
|
||||
/* Choose the transport for sending retransmit packet. */
|
||||
struct sctp_transport *sctp_assoc_choose_alter_transport(
|
||||
struct sctp_association *asoc, struct sctp_transport *last_sent_to)
|
||||
static struct sctp_transport *sctp_trans_elect_best(struct sctp_transport *curr,
|
||||
struct sctp_transport *best)
|
||||
{
|
||||
if (best == NULL)
|
||||
return curr;
|
||||
|
||||
return sctp_trans_score(curr) > sctp_trans_score(best) ? curr : best;
|
||||
}
|
||||
|
||||
void sctp_assoc_update_retran_path(struct sctp_association *asoc)
|
||||
{
|
||||
struct sctp_transport *trans = asoc->peer.retran_path;
|
||||
struct sctp_transport *trans_next = NULL;
|
||||
|
||||
/* We're done as we only have the one and only path. */
|
||||
if (asoc->peer.transport_count == 1)
|
||||
return;
|
||||
/* If active_path and retran_path are the same and active,
|
||||
* then this is the only active path. Use it.
|
||||
*/
|
||||
if (asoc->peer.active_path == asoc->peer.retran_path &&
|
||||
asoc->peer.active_path->state == SCTP_ACTIVE)
|
||||
return;
|
||||
|
||||
/* Iterate from retran_path's successor back to retran_path. */
|
||||
for (trans = list_next_entry(trans, transports); 1;
|
||||
trans = list_next_entry(trans, transports)) {
|
||||
/* Manually skip the head element. */
|
||||
if (&trans->transports == &asoc->peer.transport_addr_list)
|
||||
continue;
|
||||
if (trans->state == SCTP_UNCONFIRMED)
|
||||
continue;
|
||||
trans_next = sctp_trans_elect_best(trans, trans_next);
|
||||
/* Active is good enough for immediate return. */
|
||||
if (trans_next->state == SCTP_ACTIVE)
|
||||
break;
|
||||
/* We've reached the end, time to update path. */
|
||||
if (trans == asoc->peer.retran_path)
|
||||
break;
|
||||
}
|
||||
|
||||
if (trans_next != NULL)
|
||||
asoc->peer.retran_path = trans_next;
|
||||
|
||||
SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_update_retran_path:association"
|
||||
" %p updated new path to addr: ",
|
||||
" port: %d\n",
|
||||
asoc,
|
||||
(&asoc->peer.retran_path->ipaddr),
|
||||
ntohs(asoc->peer.retran_path->ipaddr.v4.sin_port));
|
||||
}
|
||||
|
||||
struct sctp_transport *
|
||||
sctp_assoc_choose_alter_transport(struct sctp_association *asoc,
|
||||
struct sctp_transport *last_sent_to)
|
||||
{
|
||||
/* If this is the first time packet is sent, use the active path,
|
||||
* else use the retran path. If the last packet was sent over the
|
||||
* retran path, update the retran path and use it.
|
||||
*/
|
||||
if (!last_sent_to)
|
||||
if (last_sent_to == NULL) {
|
||||
return asoc->peer.active_path;
|
||||
else {
|
||||
} else {
|
||||
if (last_sent_to == asoc->peer.retran_path)
|
||||
sctp_assoc_update_retran_path(asoc);
|
||||
|
||||
return asoc->peer.retran_path;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -4310,6 +4310,12 @@ int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp)
|
||||
if (!asoc)
|
||||
return -EINVAL;
|
||||
|
||||
/* If there is a thread waiting on more sndbuf space for
|
||||
* sending on this asoc, it cannot be peeled.
|
||||
*/
|
||||
if (waitqueue_active(&asoc->wait))
|
||||
return -EBUSY;
|
||||
|
||||
/* An association cannot be branched off from an already peeled-off
|
||||
* socket, nor is this supported for tcp style sockets.
|
||||
*/
|
||||
@@ -6724,7 +6730,6 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p,
|
||||
*/
|
||||
sctp_release_sock(sk);
|
||||
current_timeo = schedule_timeout(current_timeo);
|
||||
BUG_ON(sk != asoc->base.sk);
|
||||
sctp_lock_sock(sk);
|
||||
|
||||
*timeo_p = current_timeo;
|
||||
|
||||
+3
-1
@@ -2389,8 +2389,10 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
|
||||
return err;
|
||||
|
||||
err = sock_error(sock->sk);
|
||||
if (err)
|
||||
if (err) {
|
||||
datagrams = err;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
entry = mmsg;
|
||||
compat_entry = (struct compat_mmsghdr __user *)mmsg;
|
||||
|
||||
@@ -260,7 +260,7 @@ static int gssx_dec_option_array(struct xdr_stream *xdr,
|
||||
if (!oa->data)
|
||||
return -ENOMEM;
|
||||
|
||||
creds = kmalloc(sizeof(struct svc_cred), GFP_KERNEL);
|
||||
creds = kzalloc(sizeof(struct svc_cred), GFP_KERNEL);
|
||||
if (!creds) {
|
||||
kfree(oa->data);
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1518,7 +1518,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
|
||||
case RPC_GSS_PROC_DESTROY:
|
||||
if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
|
||||
goto auth_err;
|
||||
rsci->h.expiry_time = get_seconds();
|
||||
rsci->h.expiry_time = seconds_since_boot();
|
||||
set_bit(CACHE_NEGATIVE, &rsci->h.flags);
|
||||
if (resv->iov_len + 4 > PAGE_SIZE)
|
||||
goto drop;
|
||||
|
||||
+9
-9
@@ -152,6 +152,7 @@ void unix_notinflight(struct user_struct *user, struct file *fp)
|
||||
if (s) {
|
||||
struct unix_sock *u = unix_sk(s);
|
||||
|
||||
BUG_ON(!atomic_long_read(&u->inflight));
|
||||
BUG_ON(list_empty(&u->link));
|
||||
if (atomic_long_dec_and_test(&u->inflight))
|
||||
list_del_init(&u->link);
|
||||
@@ -358,6 +359,14 @@ void unix_gc(void)
|
||||
}
|
||||
list_del(&cursor);
|
||||
|
||||
/* Now gc_candidates contains only garbage. Restore original
|
||||
* inflight counters for these as well, and remove the skbuffs
|
||||
* which are creating the cycle(s).
|
||||
*/
|
||||
skb_queue_head_init(&hitlist);
|
||||
list_for_each_entry(u, &gc_candidates, link)
|
||||
scan_children(&u->sk, inc_inflight, &hitlist);
|
||||
|
||||
/*
|
||||
* not_cycle_list contains those sockets which do not make up a
|
||||
* cycle. Restore these to the inflight list.
|
||||
@@ -368,15 +377,6 @@ void unix_gc(void)
|
||||
list_move_tail(&u->link, &gc_inflight_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now gc_candidates contains only garbage. Restore original
|
||||
* inflight counters for these as well, and remove the skbuffs
|
||||
* which are creating the cycle(s).
|
||||
*/
|
||||
skb_queue_head_init(&hitlist);
|
||||
list_for_each_entry(u, &gc_candidates, link)
|
||||
scan_children(&u->sk, inc_inflight, &hitlist);
|
||||
|
||||
spin_unlock(&unix_gc_lock);
|
||||
|
||||
/* Here we are. Hitlist is filled. Die. */
|
||||
|
||||
Reference in New Issue
Block a user