diff options
Diffstat (limited to 'net')
35 files changed, 246 insertions, 174 deletions
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index 5af18d11b51..2a167658bb9 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -192,10 +192,10 @@ static int pack_sg_list(struct scatterlist *sg, int start,  		s = rest_of_page(data);  		if (s > count)  			s = count; +		BUG_ON(index > limit);  		sg_set_buf(&sg[index++], data, s);  		count -= s;  		data += s; -		BUG_ON(index > limit);  	}  	return index-start; diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 0301b328cf0..86852963b7f 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -1208,9 +1208,7 @@ static int atalk_connect(struct socket *sock, struct sockaddr *uaddr,  	if (addr->sat_addr.s_node == ATADDR_BCAST &&  	    !sock_flag(sk, SOCK_BROADCAST)) {  #if 1 -		printk(KERN_WARNING "%s is broken and did not set " -				    "SO_BROADCAST. It will break when 2.2 is " -				    "released.\n", +		pr_warn("atalk_connect: %s is broken and did not set SO_BROADCAST.\n",  			current->comm);  #else  		return -EACCES; diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 46e7f86acfc..3e18af4dadc 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -210,7 +210,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)  		}  		if (sk->sk_state == BT_CONNECTED || !newsock || -		    test_bit(BT_DEFER_SETUP, &bt_sk(parent)->flags)) { +		    test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags)) {  			bt_accept_unlink(sk);  			if (newsock)  				sock_graft(sk, newsock); diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index a776f751edb..ba4323bce0e 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -504,13 +504,6 @@ void ceph_destroy_client(struct ceph_client *client)  	/* unmount */  	ceph_osdc_stop(&client->osdc); -	/* -	 * make sure osd connections close out before destroying the -	 * auth module, which is needed to free those connections' -	 * ceph_authorizers. -	 */ -	ceph_msgr_flush(); -  	ceph_monc_stop(&client->monc);  	ceph_debugfs_client_cleanup(client); diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 524f4e4f598..b332c3d7605 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -563,6 +563,10 @@ static void prepare_write_message(struct ceph_connection *con)  		m->hdr.seq = cpu_to_le64(++con->out_seq);  		m->needs_out_seq = false;  	} +#ifdef CONFIG_BLOCK +	else +		m->bio_iter = NULL; +#endif  	dout("prepare_write_message %p seq %lld type %d len %d+%d+%d %d pgs\n",  	     m, con->out_seq, le16_to_cpu(m->hdr.type), diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c index 10d6008d31f..d0649a9655b 100644 --- a/net/ceph/mon_client.c +++ b/net/ceph/mon_client.c @@ -847,6 +847,14 @@ void ceph_monc_stop(struct ceph_mon_client *monc)  	mutex_unlock(&monc->mutex); +	/* +	 * flush msgr queue before we destroy ourselves to ensure that: +	 *  - any work that references our embedded con is finished. +	 *  - any osd_client or other work that may reference an authorizer +	 *    finishes before we shut down the auth subsystem. +	 */ +	ceph_msgr_flush(); +  	ceph_auth_destroy(monc->auth);  	ceph_msg_put(monc->m_auth); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 1ffebed5ce0..ca59e66c978 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -139,15 +139,15 @@ void ceph_osdc_release_request(struct kref *kref)  	if (req->r_request)  		ceph_msg_put(req->r_request); -	if (req->r_reply) -		ceph_msg_put(req->r_reply);  	if (req->r_con_filling_msg) {  		dout("release_request revoking pages %p from con %p\n",  		     req->r_pages, req->r_con_filling_msg);  		ceph_con_revoke_message(req->r_con_filling_msg,  				      req->r_reply); -		ceph_con_put(req->r_con_filling_msg); +		req->r_con_filling_msg->ops->put(req->r_con_filling_msg);  	} +	if (req->r_reply) +		ceph_msg_put(req->r_reply);  	if (req->r_own_pages)  		ceph_release_page_vector(req->r_pages,  					 req->r_num_pages); @@ -1216,7 +1216,7 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg,  	if (req->r_con_filling_msg == con && req->r_reply == msg) {  		dout(" dropping con_filling_msg ref %p\n", con);  		req->r_con_filling_msg = NULL; -		ceph_con_put(con); +		con->ops->put(con);  	}  	if (!req->r_got_reply) { @@ -2028,7 +2028,7 @@ static struct ceph_msg *get_reply(struct ceph_connection *con,  		dout("get_reply revoking msg %p from old con %p\n",  		     req->r_reply, req->r_con_filling_msg);  		ceph_con_revoke_message(req->r_con_filling_msg, req->r_reply); -		ceph_con_put(req->r_con_filling_msg); +		req->r_con_filling_msg->ops->put(req->r_con_filling_msg);  		req->r_con_filling_msg = NULL;  	} @@ -2063,7 +2063,7 @@ static struct ceph_msg *get_reply(struct ceph_connection *con,  #endif  	}  	*skip = 0; -	req->r_con_filling_msg = ceph_con_get(con); +	req->r_con_filling_msg = con->ops->get(con);  	dout("get_reply tid %lld %p\n", tid, m);  out: diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index ea5fb9fcc3f..d23b6682f4e 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -36,9 +36,6 @@  #define TRACE_ON 1  #define TRACE_OFF 0 -static void send_dm_alert(struct work_struct *unused); - -  /*   * Globals, our netlink socket pointer   * and the work handle that will send up @@ -48,11 +45,10 @@ static int trace_state = TRACE_OFF;  static DEFINE_MUTEX(trace_state_mutex);  struct per_cpu_dm_data { -	struct work_struct dm_alert_work; -	struct sk_buff __rcu *skb; -	atomic_t dm_hit_count; -	struct timer_list send_timer; -	int cpu; +	spinlock_t		lock; +	struct sk_buff		*skb; +	struct work_struct	dm_alert_work; +	struct timer_list	send_timer;  };  struct dm_hw_stat_delta { @@ -78,13 +74,13 @@ static int dm_delay = 1;  static unsigned long dm_hw_check_delta = 2*HZ;  static LIST_HEAD(hw_stats_list); -static void reset_per_cpu_data(struct per_cpu_dm_data *data) +static struct sk_buff *reset_per_cpu_data(struct per_cpu_dm_data *data)  {  	size_t al;  	struct net_dm_alert_msg *msg;  	struct nlattr *nla;  	struct sk_buff *skb; -	struct sk_buff *oskb = rcu_dereference_protected(data->skb, 1); +	unsigned long flags;  	al = sizeof(struct net_dm_alert_msg);  	al += dm_hit_limit * sizeof(struct net_dm_drop_point); @@ -99,65 +95,40 @@ static void reset_per_cpu_data(struct per_cpu_dm_data *data)  				  sizeof(struct net_dm_alert_msg));  		msg = nla_data(nla);  		memset(msg, 0, al); -	} else -		schedule_work_on(data->cpu, &data->dm_alert_work); - -	/* -	 * Don't need to lock this, since we are guaranteed to only -	 * run this on a single cpu at a time. -	 * Note also that we only update data->skb if the old and new skb -	 * pointers don't match.  This ensures that we don't continually call -	 * synchornize_rcu if we repeatedly fail to alloc a new netlink message. -	 */ -	if (skb != oskb) { -		rcu_assign_pointer(data->skb, skb); - -		synchronize_rcu(); - -		atomic_set(&data->dm_hit_count, dm_hit_limit); +	} else { +		mod_timer(&data->send_timer, jiffies + HZ / 10);  	} +	spin_lock_irqsave(&data->lock, flags); +	swap(data->skb, skb); +	spin_unlock_irqrestore(&data->lock, flags); + +	return skb;  } -static void send_dm_alert(struct work_struct *unused) +static void send_dm_alert(struct work_struct *work)  {  	struct sk_buff *skb; -	struct per_cpu_dm_data *data = &get_cpu_var(dm_cpu_data); +	struct per_cpu_dm_data *data; -	WARN_ON_ONCE(data->cpu != smp_processor_id()); +	data = container_of(work, struct per_cpu_dm_data, dm_alert_work); -	/* -	 * Grab the skb we're about to send -	 */ -	skb = rcu_dereference_protected(data->skb, 1); - -	/* -	 * Replace it with a new one -	 */ -	reset_per_cpu_data(data); +	skb = reset_per_cpu_data(data); -	/* -	 * Ship it! -	 */  	if (skb)  		genlmsg_multicast(skb, 0, NET_DM_GRP_ALERT, GFP_KERNEL); - -	put_cpu_var(dm_cpu_data);  }  /*   * This is the timer function to delay the sending of an alert   * in the event that more drops will arrive during the - * hysteresis period.  Note that it operates under the timer interrupt - * so we don't need to disable preemption here + * hysteresis period.   */ -static void sched_send_work(unsigned long unused) +static void sched_send_work(unsigned long _data)  { -	struct per_cpu_dm_data *data =  &get_cpu_var(dm_cpu_data); - -	schedule_work_on(smp_processor_id(), &data->dm_alert_work); +	struct per_cpu_dm_data *data = (struct per_cpu_dm_data *)_data; -	put_cpu_var(dm_cpu_data); +	schedule_work(&data->dm_alert_work);  }  static void trace_drop_common(struct sk_buff *skb, void *location) @@ -167,33 +138,28 @@ static void trace_drop_common(struct sk_buff *skb, void *location)  	struct nlattr *nla;  	int i;  	struct sk_buff *dskb; -	struct per_cpu_dm_data *data = &get_cpu_var(dm_cpu_data); - +	struct per_cpu_dm_data *data; +	unsigned long flags; -	rcu_read_lock(); -	dskb = rcu_dereference(data->skb); +	local_irq_save(flags); +	data = &__get_cpu_var(dm_cpu_data); +	spin_lock(&data->lock); +	dskb = data->skb;  	if (!dskb)  		goto out; -	if (!atomic_add_unless(&data->dm_hit_count, -1, 0)) { -		/* -		 * we're already at zero, discard this hit -		 */ -		goto out; -	} -  	nlh = (struct nlmsghdr *)dskb->data;  	nla = genlmsg_data(nlmsg_data(nlh));  	msg = nla_data(nla);  	for (i = 0; i < msg->entries; i++) {  		if (!memcmp(&location, msg->points[i].pc, sizeof(void *))) {  			msg->points[i].count++; -			atomic_inc(&data->dm_hit_count);  			goto out;  		}  	} - +	if (msg->entries == dm_hit_limit) +		goto out;  	/*  	 * We need to create a new entry  	 */ @@ -205,13 +171,11 @@ static void trace_drop_common(struct sk_buff *skb, void *location)  	if (!timer_pending(&data->send_timer)) {  		data->send_timer.expires = jiffies + dm_delay * HZ; -		add_timer_on(&data->send_timer, smp_processor_id()); +		add_timer(&data->send_timer);  	}  out: -	rcu_read_unlock(); -	put_cpu_var(dm_cpu_data); -	return; +	spin_unlock_irqrestore(&data->lock, flags);  }  static void trace_kfree_skb_hit(void *ignore, struct sk_buff *skb, void *location) @@ -418,11 +382,11 @@ static int __init init_net_drop_monitor(void)  	for_each_possible_cpu(cpu) {  		data = &per_cpu(dm_cpu_data, cpu); -		data->cpu = cpu;  		INIT_WORK(&data->dm_alert_work, send_dm_alert);  		init_timer(&data->send_timer); -		data->send_timer.data = cpu; +		data->send_timer.data = (unsigned long)data;  		data->send_timer.function = sched_send_work; +		spin_lock_init(&data->lock);  		reset_per_cpu_data(data);  	} diff --git a/net/core/filter.c b/net/core/filter.c index a3eddb515d1..d4ce2dc712e 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -616,9 +616,9 @@ static int __sk_prepare_filter(struct sk_filter *fp)  /**   *	sk_unattached_filter_create - create an unattached filter   *	@fprog: the filter program - *	@sk: the socket to use + *	@pfp: the unattached filter that is created   * - * Create a filter independent ofr any socket. We first run some + * Create a filter independent of any socket. We first run some   * sanity checks on it to make sure it does not explode on us later.   * If an error occurs or there is insufficient memory for the filter   * a negative errno code is returned. On success the return is zero. diff --git a/net/core/neighbour.c b/net/core/neighbour.c index eb09f8bbbf0..d81d026138f 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -2219,9 +2219,7 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,  	rcu_read_lock_bh();  	nht = rcu_dereference_bh(tbl->nht); -	for (h = 0; h < (1 << nht->hash_shift); h++) { -		if (h < s_h) -			continue; +	for (h = s_h; h < (1 << nht->hash_shift); h++) {  		if (h > s_h)  			s_idx = 0;  		for (n = rcu_dereference_bh(nht->hash_buckets[h]), idx = 0; @@ -2260,9 +2258,7 @@ static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,  	read_lock_bh(&tbl->lock); -	for (h = 0; h <= PNEIGH_HASHMASK; h++) { -		if (h < s_h) -			continue; +	for (h = s_h; h <= PNEIGH_HASHMASK; h++) {  		if (h > s_h)  			s_idx = 0;  		for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) { @@ -2297,7 +2293,7 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)  	struct neigh_table *tbl;  	int t, family, s_t;  	int proxy = 0; -	int err = 0; +	int err;  	read_lock(&neigh_tbl_lock);  	family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family; @@ -2311,7 +2307,7 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)  	s_t = cb->args[0]; -	for (tbl = neigh_tables, t = 0; tbl && (err >= 0); +	for (tbl = neigh_tables, t = 0; tbl;  	     tbl = tbl->next, t++) {  		if (t < s_t || (family && tbl->family != family))  			continue; @@ -2322,6 +2318,8 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)  			err = pneigh_dump_table(tbl, skb, cb);  		else  			err = neigh_dump_table(tbl, skb, cb); +		if (err < 0) +			break;  	}  	read_unlock(&neigh_tbl_lock); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 3d84fb9d887..f9f40b932e4 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -362,22 +362,23 @@ EXPORT_SYMBOL(netpoll_send_skb_on_dev);  void netpoll_send_udp(struct netpoll *np, const char *msg, int len)  { -	int total_len, eth_len, ip_len, udp_len; +	int total_len, ip_len, udp_len;  	struct sk_buff *skb;  	struct udphdr *udph;  	struct iphdr *iph;  	struct ethhdr *eth;  	udp_len = len + sizeof(*udph); -	ip_len = eth_len = udp_len + sizeof(*iph); -	total_len = eth_len + ETH_HLEN + NET_IP_ALIGN; +	ip_len = udp_len + sizeof(*iph); +	total_len = ip_len + LL_RESERVED_SPACE(np->dev); -	skb = find_skb(np, total_len, total_len - len); +	skb = find_skb(np, total_len + np->dev->needed_tailroom, +		       total_len - len);  	if (!skb)  		return;  	skb_copy_to_linear_data(skb, msg, len); -	skb->len += len; +	skb_put(skb, len);  	skb_push(skb, sizeof(*udph));  	skb_reset_transport_header(skb); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 016694d6248..d78671e9d54 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3361,7 +3361,7 @@ EXPORT_SYMBOL(kfree_skb_partial);   * @to: prior buffer   * @from: buffer to add   * @fragstolen: pointer to boolean - * + * @delta_truesize: how much more was allocated than was requested   */  bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,  		      bool *fragstolen, int *delta_truesize) diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index d4d61b694fa..dfba343b250 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -560,6 +560,17 @@ bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout)  }  EXPORT_SYMBOL(inet_peer_xrlim_allow); +static void inetpeer_inval_rcu(struct rcu_head *head) +{ +	struct inet_peer *p = container_of(head, struct inet_peer, gc_rcu); + +	spin_lock_bh(&gc_lock); +	list_add_tail(&p->gc_list, &gc_list); +	spin_unlock_bh(&gc_lock); + +	schedule_delayed_work(&gc_work, gc_delay); +} +  void inetpeer_invalidate_tree(int family)  {  	struct inet_peer *old, *new, *prev; @@ -576,10 +587,7 @@ void inetpeer_invalidate_tree(int family)  	prev = cmpxchg(&base->root, old, new);  	if (prev == old) {  		base->total = 0; -		spin_lock(&gc_lock); -		list_add_tail(&prev->gc_list, &gc_list); -		spin_unlock(&gc_lock); -		schedule_delayed_work(&gc_work, gc_delay); +		call_rcu(&prev->gc_rcu, inetpeer_inval_rcu);  	}  out: diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index e5c44fc586a..ab09b126423 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -44,6 +44,7 @@ static int ip_forward_finish(struct sk_buff *skb)  	struct ip_options *opt	= &(IPCB(skb)->opt);  	IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS); +	IP_ADD_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTOCTETS, skb->len);  	if (unlikely(opt->optlen))  		ip_forward_options(skb); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index a9e519ad6db..c94bbc6f2ba 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1574,6 +1574,7 @@ static inline int ipmr_forward_finish(struct sk_buff *skb)  	struct ip_options *opt = &(IPCB(skb)->opt);  	IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS); +	IP_ADD_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTOCTETS, skb->len);  	if (unlikely(opt->optlen))  		ip_forward_options(skb); diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 0c220a41662..74c21b924a7 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1561,7 +1561,7 @@ static int fib6_age(struct rt6_info *rt, void *arg)  				neigh_flags = neigh->flags;  				neigh_release(neigh);  			} -			if (neigh_flags & NTF_ROUTER) { +			if (!(neigh_flags & NTF_ROUTER)) {  				RT6_TRACE("purging route %p via non-router but gateway\n",  					  rt);  				return -1; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 17b8c67998b..decc21d19c5 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -526,6 +526,7 @@ int ip6_forward(struct sk_buff *skb)  	hdr->hop_limit--;  	IP6_INC_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS); +	IP6_ADD_STATS_BH(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len);  	return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, skb, skb->dev, dst->dev,  		       ip6_forward_finish); diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index b15dc08643a..461e47c8e95 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1886,6 +1886,8 @@ static inline int ip6mr_forward2_finish(struct sk_buff *skb)  {  	IP6_INC_STATS_BH(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)),  			 IPSTATS_MIB_OUTFORWDATAGRAMS); +	IP6_ADD_STATS_BH(dev_net(skb_dst(skb)->dev), ip6_dst_idev(skb_dst(skb)), +			 IPSTATS_MIB_OUTOCTETS, skb->len);  	return dst_output(skb);  } diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index 443591d629c..185f12f4a5f 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -162,6 +162,7 @@ static void l2tp_eth_delete(struct l2tp_session *session)  		if (dev) {  			unregister_netdev(dev);  			spriv->dev = NULL; +			module_put(THIS_MODULE);  		}  	}  } @@ -249,6 +250,7 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p  	if (rc < 0)  		goto out_del_dev; +	__module_get(THIS_MODULE);  	/* Must be done after register_netdev() */  	strlcpy(session->ifname, dev->name, IFNAMSIZ); diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 70614e7affa..61d8b75d268 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -464,10 +464,12 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m  					   sk->sk_bound_dev_if);  		if (IS_ERR(rt))  			goto no_route; -		if (connected) +		if (connected) {  			sk_setup_caps(sk, &rt->dst); -		else -			dst_release(&rt->dst); /* safe since we hold rcu_read_lock */ +		} else { +			skb_dst_set(skb, &rt->dst); +			goto xmit; +		}  	}  	/* We dont need to clone dst here, it is guaranteed to not disappear. @@ -475,6 +477,7 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m  	 */  	skb_dst_set_noref(skb, &rt->dst); +xmit:  	/* Queue the packet to IP for output */  	rc = ip_queue_xmit(skb, &inet->cork.fl);  	rcu_read_unlock(); diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 26ddb699d69..c649188314c 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -145,15 +145,20 @@ static void sta_rx_agg_session_timer_expired(unsigned long data)  	struct tid_ampdu_rx *tid_rx;  	unsigned long timeout; +	rcu_read_lock();  	tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[*ptid]); -	if (!tid_rx) +	if (!tid_rx) { +		rcu_read_unlock();  		return; +	}  	timeout = tid_rx->last_rx + TU_TO_JIFFIES(tid_rx->timeout);  	if (time_is_after_jiffies(timeout)) {  		mod_timer(&tid_rx->session_timer, timeout); +		rcu_read_unlock();  		return;  	} +	rcu_read_unlock();  #ifdef CONFIG_MAC80211_HT_DEBUG  	printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 495831ee48f..e9cecca5c44 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -533,16 +533,16 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy,  		sinfo.filled = 0;  		sta_set_sinfo(sta, &sinfo); -		if (sinfo.filled | STATION_INFO_TX_BITRATE) +		if (sinfo.filled & STATION_INFO_TX_BITRATE)  			data[i] = 100000 *  				cfg80211_calculate_bitrate(&sinfo.txrate);  		i++; -		if (sinfo.filled | STATION_INFO_RX_BITRATE) +		if (sinfo.filled & STATION_INFO_RX_BITRATE)  			data[i] = 100000 *  				cfg80211_calculate_bitrate(&sinfo.rxrate);  		i++; -		if (sinfo.filled | STATION_INFO_SIGNAL_AVG) +		if (sinfo.filled & STATION_INFO_SIGNAL_AVG)  			data[i] = (u8)sinfo.signal_avg;  		i++;  	} else { diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d4c19a7773d..8664111d056 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -637,6 +637,18 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,  		ieee80211_configure_filter(local);  		break;  	default: +		mutex_lock(&local->mtx); +		if (local->hw_roc_dev == sdata->dev && +		    local->hw_roc_channel) { +			/* ignore return value since this is racy */ +			drv_cancel_remain_on_channel(local); +			ieee80211_queue_work(&local->hw, &local->hw_roc_done); +		} +		mutex_unlock(&local->mtx); + +		flush_work(&local->hw_roc_start); +		flush_work(&local->hw_roc_done); +  		flush_work(&sdata->work);  		/*  		 * When we get here, the interface is marked down. diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 04c30630898..91d84cc77bb 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1220,6 +1220,22 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,  	sdata->vif.bss_conf.qos = true;  } +static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) +{ +	lockdep_assert_held(&sdata->local->mtx); + +	sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL | +				IEEE80211_STA_BEACON_POLL); +	ieee80211_run_deferred_scan(sdata->local); +} + +static void ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) +{ +	mutex_lock(&sdata->local->mtx); +	__ieee80211_stop_poll(sdata); +	mutex_unlock(&sdata->local->mtx); +} +  static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,  					   u16 capab, bool erp_valid, u8 erp)  { @@ -1285,8 +1301,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,  	sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;  	/* just to be sure */ -	sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL | -				IEEE80211_STA_BEACON_POLL); +	ieee80211_stop_poll(sdata);  	ieee80211_led_assoc(local, 1); @@ -1456,8 +1471,7 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)  		return;  	} -	ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL | -			  IEEE80211_STA_BEACON_POLL); +	__ieee80211_stop_poll(sdata);  	mutex_lock(&local->iflist_mtx);  	ieee80211_recalc_ps(local, -1); @@ -1477,7 +1491,6 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)  		  round_jiffies_up(jiffies +  				   IEEE80211_CONNECTION_IDLE_TIME));  out: -	ieee80211_run_deferred_scan(local);  	mutex_unlock(&local->mtx);  } @@ -2408,7 +2421,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  		net_dbg_ratelimited("%s: cancelling probereq poll due to a received beacon\n",  				    sdata->name);  #endif +		mutex_lock(&local->mtx);  		ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL; +		ieee80211_run_deferred_scan(local); +		mutex_unlock(&local->mtx); +  		mutex_lock(&local->iflist_mtx);  		ieee80211_recalc_ps(local, -1);  		mutex_unlock(&local->iflist_mtx); @@ -2595,8 +2612,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	u8 frame_buf[DEAUTH_DISASSOC_LEN]; -	ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL | -			  IEEE80211_STA_BEACON_POLL); +	ieee80211_stop_poll(sdata);  	ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,  			       false, frame_buf); @@ -2874,8 +2890,7 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)  	u32 flags;  	if (sdata->vif.type == NL80211_IFTYPE_STATION) { -		sdata->u.mgd.flags &= ~(IEEE80211_STA_BEACON_POLL | -					IEEE80211_STA_CONNECTION_POLL); +		__ieee80211_stop_poll(sdata);  		/* let's probe the connection once */  		flags = sdata->local->hw.flags; @@ -2944,7 +2959,10 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)  	if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running))  		add_timer(&ifmgd->chswitch_timer);  	ieee80211_sta_reset_beacon_monitor(sdata); + +	mutex_lock(&sdata->local->mtx);  	ieee80211_restart_sta_timer(sdata); +	mutex_unlock(&sdata->local->mtx);  }  #endif @@ -3106,7 +3124,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,  	}  	local->oper_channel = cbss->channel; -	ieee80211_hw_config(local, 0); +	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);  	if (!have_sta) {  		u32 rates = 0, basic_rates = 0; diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index f054e94901a..935aa4b6dee 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -234,6 +234,22 @@ static void ieee80211_hw_roc_done(struct work_struct *work)  		return;  	} +	/* was never transmitted */ +	if (local->hw_roc_skb) { +		u64 cookie; + +		cookie = local->hw_roc_cookie ^ 2; + +		cfg80211_mgmt_tx_status(local->hw_roc_dev, cookie, +					local->hw_roc_skb->data, +					local->hw_roc_skb->len, false, +					GFP_KERNEL); + +		kfree_skb(local->hw_roc_skb); +		local->hw_roc_skb = NULL; +		local->hw_roc_skb_for_status = NULL; +	} +  	if (!local->hw_roc_for_tx)  		cfg80211_remain_on_channel_expired(local->hw_roc_dev,  						   local->hw_roc_cookie, diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index f5b1638fbf8..de455f8bbb9 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -378,7 +378,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)  	/* make the station visible */  	sta_info_hash_add(local, sta); -	list_add(&sta->list, &local->sta_list); +	list_add_rcu(&sta->list, &local->sta_list);  	set_sta_flag(sta, WLAN_STA_INSERTED); @@ -688,7 +688,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)  	if (ret)  		return ret; -	list_del(&sta->list); +	list_del_rcu(&sta->list);  	mutex_lock(&local->key_mtx);  	for (i = 0; i < NUM_DEFAULT_KEYS; i++) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 847215bb2a6..e453212fa17 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1737,7 +1737,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  	__le16 fc;  	struct ieee80211_hdr hdr;  	struct ieee80211s_hdr mesh_hdr __maybe_unused; -	struct mesh_path __maybe_unused *mppath = NULL; +	struct mesh_path __maybe_unused *mppath = NULL, *mpath = NULL;  	const u8 *encaps_data;  	int encaps_len, skip_header_bytes;  	int nh_pos, h_pos; @@ -1803,8 +1803,11 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,  			goto fail;  		}  		rcu_read_lock(); -		if (!is_multicast_ether_addr(skb->data)) -			mppath = mpp_path_lookup(skb->data, sdata); +		if (!is_multicast_ether_addr(skb->data)) { +			mpath = mesh_path_lookup(skb->data, sdata); +			if (!mpath) +				mppath = mpp_path_lookup(skb->data, sdata); +		}  		/*  		 * Use address extension if it is a packet from diff --git a/net/mac80211/util.c b/net/mac80211/util.c index a44c6807df0..8dd4712620f 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1271,7 +1271,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)  			enum ieee80211_sta_state state;  			for (state = IEEE80211_STA_NOTEXIST; -			     state < sta->sta_state - 1; state++) +			     state < sta->sta_state; state++)  				WARN_ON(drv_sta_state(local, sta->sdata, sta,  						      state, state + 1));  		} diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index 46d69d7f1bb..31f50bc3a31 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -270,9 +270,8 @@ static int expect_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,  		return 0;  	/* RTP port is even */ -	port &= htons(~1); -	rtp_port = port; -	rtcp_port = htons(ntohs(port) + 1); +	rtp_port = port & ~htons(1); +	rtcp_port = port | htons(1);  	/* Create expect for RTP */  	if ((rtp_exp = nf_ct_expect_alloc(ct)) == NULL) diff --git a/net/netfilter/xt_HMARK.c b/net/netfilter/xt_HMARK.c index 0a96a43108e..1686ca1b53a 100644 --- a/net/netfilter/xt_HMARK.c +++ b/net/netfilter/xt_HMARK.c @@ -32,13 +32,13 @@ MODULE_ALIAS("ipt_HMARK");  MODULE_ALIAS("ip6t_HMARK");  struct hmark_tuple { -	u32			src; -	u32			dst; +	__be32			src; +	__be32			dst;  	union hmark_ports	uports; -	uint8_t			proto; +	u8			proto;  }; -static inline u32 hmark_addr6_mask(const __u32 *addr32, const __u32 *mask) +static inline __be32 hmark_addr6_mask(const __be32 *addr32, const __be32 *mask)  {  	return (addr32[0] & mask[0]) ^  	       (addr32[1] & mask[1]) ^ @@ -46,8 +46,8 @@ static inline u32 hmark_addr6_mask(const __u32 *addr32, const __u32 *mask)  	       (addr32[3] & mask[3]);  } -static inline u32 -hmark_addr_mask(int l3num, const __u32 *addr32, const __u32 *mask) +static inline __be32 +hmark_addr_mask(int l3num, const __be32 *addr32, const __be32 *mask)  {  	switch (l3num) {  	case AF_INET: @@ -58,6 +58,22 @@ hmark_addr_mask(int l3num, const __u32 *addr32, const __u32 *mask)  	return 0;  } +static inline void hmark_swap_ports(union hmark_ports *uports, +				    const struct xt_hmark_info *info) +{ +	union hmark_ports hp; +	u16 src, dst; + +	hp.b32 = (uports->b32 & info->port_mask.b32) | info->port_set.b32; +	src = ntohs(hp.b16.src); +	dst = ntohs(hp.b16.dst); + +	if (dst > src) +		uports->v32 = (dst << 16) | src; +	else +		uports->v32 = (src << 16) | dst; +} +  static int  hmark_ct_set_htuple(const struct sk_buff *skb, struct hmark_tuple *t,  		    const struct xt_hmark_info *info) @@ -74,22 +90,19 @@ hmark_ct_set_htuple(const struct sk_buff *skb, struct hmark_tuple *t,  	otuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;  	rtuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; -	t->src = hmark_addr_mask(otuple->src.l3num, otuple->src.u3.all, -				 info->src_mask.all); -	t->dst = hmark_addr_mask(otuple->src.l3num, rtuple->src.u3.all, -				 info->dst_mask.all); +	t->src = hmark_addr_mask(otuple->src.l3num, otuple->src.u3.ip6, +				 info->src_mask.ip6); +	t->dst = hmark_addr_mask(otuple->src.l3num, rtuple->src.u3.ip6, +				 info->dst_mask.ip6);  	if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))  		return 0;  	t->proto = nf_ct_protonum(ct);  	if (t->proto != IPPROTO_ICMP) { -		t->uports.p16.src = otuple->src.u.all; -		t->uports.p16.dst = rtuple->src.u.all; -		t->uports.v32 = (t->uports.v32 & info->port_mask.v32) | -				info->port_set.v32; -		if (t->uports.p16.dst < t->uports.p16.src) -			swap(t->uports.p16.dst, t->uports.p16.src); +		t->uports.b16.src = otuple->src.u.all; +		t->uports.b16.dst = rtuple->src.u.all; +		hmark_swap_ports(&t->uports, info);  	}  	return 0; @@ -98,15 +111,19 @@ hmark_ct_set_htuple(const struct sk_buff *skb, struct hmark_tuple *t,  #endif  } +/* This hash function is endian independent, to ensure consistent hashing if + * the cluster is composed of big and little endian systems. */  static inline u32  hmark_hash(struct hmark_tuple *t, const struct xt_hmark_info *info)  {  	u32 hash; +	u32 src = ntohl(t->src); +	u32 dst = ntohl(t->dst); -	if (t->dst < t->src) -		swap(t->src, t->dst); +	if (dst < src) +		swap(src, dst); -	hash = jhash_3words(t->src, t->dst, t->uports.v32, info->hashrnd); +	hash = jhash_3words(src, dst, t->uports.v32, info->hashrnd);  	hash = hash ^ (t->proto & info->proto_mask);  	return (((u64)hash * info->hmodulus) >> 32) + info->hoffset; @@ -126,11 +143,7 @@ hmark_set_tuple_ports(const struct sk_buff *skb, unsigned int nhoff,  	if (skb_copy_bits(skb, nhoff, &t->uports, sizeof(t->uports)) < 0)  		return; -	t->uports.v32 = (t->uports.v32 & info->port_mask.v32) | -			info->port_set.v32; - -	if (t->uports.p16.dst < t->uports.p16.src) -		swap(t->uports.p16.dst, t->uports.p16.src); +	hmark_swap_ports(&t->uports, info);  }  #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) @@ -178,8 +191,8 @@ hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t,  			return -1;  	}  noicmp: -	t->src = hmark_addr6_mask(ip6->saddr.s6_addr32, info->src_mask.all); -	t->dst = hmark_addr6_mask(ip6->daddr.s6_addr32, info->dst_mask.all); +	t->src = hmark_addr6_mask(ip6->saddr.s6_addr32, info->src_mask.ip6); +	t->dst = hmark_addr6_mask(ip6->daddr.s6_addr32, info->dst_mask.ip6);  	if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))  		return 0; @@ -255,11 +268,8 @@ hmark_pkt_set_htuple_ipv4(const struct sk_buff *skb, struct hmark_tuple *t,  		}  	} -	t->src = (__force u32) ip->saddr; -	t->dst = (__force u32) ip->daddr; - -	t->src &= info->src_mask.ip; -	t->dst &= info->dst_mask.ip; +	t->src = ip->saddr & info->src_mask.ip; +	t->dst = ip->daddr & info->dst_mask.ip;  	if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))  		return 0; diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c index 3f339b19d14..17a707db40e 100644 --- a/net/nfc/llcp/sock.c +++ b/net/nfc/llcp/sock.c @@ -292,6 +292,9 @@ static int llcp_sock_getname(struct socket *sock, struct sockaddr *addr,  	pr_debug("%p\n", sk); +	if (llcp_sock == NULL) +		return -EBADFD; +  	addr->sa_family = AF_NFC;  	*len = sizeof(struct sockaddr_nfc_llcp); diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 04040476082..21fde99e5c5 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -71,7 +71,9 @@ static void rpc_purge_list(wait_queue_head_t *waitq, struct list_head *head,  		msg->errno = err;  		destroy_msg(msg);  	} while (!list_empty(head)); -	wake_up(waitq); + +	if (waitq) +		wake_up(waitq);  }  static void @@ -91,11 +93,9 @@ rpc_timeout_upcall_queue(struct work_struct *work)  	}  	dentry = dget(pipe->dentry);  	spin_unlock(&pipe->lock); -	if (dentry) { -		rpc_purge_list(&RPC_I(dentry->d_inode)->waitq, -			       &free_list, destroy_msg, -ETIMEDOUT); -		dput(dentry); -	} +	rpc_purge_list(dentry ? &RPC_I(dentry->d_inode)->waitq : NULL, +			&free_list, destroy_msg, -ETIMEDOUT); +	dput(dentry);  }  ssize_t rpc_pipe_generic_upcall(struct file *filp, struct rpc_pipe_msg *msg, diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 7e9baaa1e54..3ee7461926d 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1374,7 +1374,8 @@ bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,  						sizeof(req->rq_snd_buf));  		return bc_send(req);  	} else { -		/* Nothing to do to drop request */ +		/* drop request */ +		xprt_free_bc_request(req);  		return 0;  	}  } diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index d2a19b0ff71..89baa332841 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -42,6 +42,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)  	cfg80211_hold_bss(bss_from_pub(bss));  	wdev->current_bss = bss_from_pub(bss); +	wdev->sme_state = CFG80211_SME_CONNECTED;  	cfg80211_upload_connect_keys(wdev);  	nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, @@ -60,7 +61,7 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)  	struct cfg80211_event *ev;  	unsigned long flags; -	CFG80211_DEV_WARN_ON(!wdev->ssid_len); +	CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);  	ev = kzalloc(sizeof(*ev), gfp);  	if (!ev) @@ -115,9 +116,11 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,  #ifdef CONFIG_CFG80211_WEXT  	wdev->wext.ibss.channel = params->channel;  #endif +	wdev->sme_state = CFG80211_SME_CONNECTING;  	err = rdev->ops->join_ibss(&rdev->wiphy, dev, params);  	if (err) {  		wdev->connect_keys = NULL; +		wdev->sme_state = CFG80211_SME_IDLE;  		return err;  	} @@ -169,6 +172,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)  	}  	wdev->current_bss = NULL; +	wdev->sme_state = CFG80211_SME_IDLE;  	wdev->ssid_len = 0;  #ifdef CONFIG_CFG80211_WEXT  	if (!nowext) diff --git a/net/wireless/util.c b/net/wireless/util.c index 55d99466bab..8f2d68fc3a4 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -935,6 +935,7 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,  				  enum nl80211_iftype iftype)  {  	struct wireless_dev *wdev_iter; +	u32 used_iftypes = BIT(iftype);  	int num[NUM_NL80211_IFTYPES];  	int total = 1;  	int i, j; @@ -961,6 +962,7 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,  		num[wdev_iter->iftype]++;  		total++; +		used_iftypes |= BIT(wdev_iter->iftype);  	}  	mutex_unlock(&rdev->devlist_mtx); @@ -970,6 +972,7 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,  	for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) {  		const struct ieee80211_iface_combination *c;  		struct ieee80211_iface_limit *limits; +		u32 all_iftypes = 0;  		c = &rdev->wiphy.iface_combinations[i]; @@ -984,6 +987,7 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,  			if (rdev->wiphy.software_iftypes & BIT(iftype))  				continue;  			for (j = 0; j < c->n_limits; j++) { +				all_iftypes |= limits[j].types;  				if (!(limits[j].types & BIT(iftype)))  					continue;  				if (limits[j].max < num[iftype]) @@ -991,7 +995,20 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,  				limits[j].max -= num[iftype];  			}  		} -		/* yay, it fits */ + +		/* +		 * Finally check that all iftypes that we're currently +		 * using are actually part of this combination. If they +		 * aren't then we can't use this combination and have +		 * to continue to the next. +		 */ +		if ((all_iftypes & used_iftypes) != used_iftypes) +			goto cont; + +		/* +		 * This combination covered all interface types and +		 * supported the requested numbers, so we're good. +		 */  		kfree(limits);  		return 0;   cont:  |