diff options
Diffstat (limited to 'net/atm/br2684.c')
| -rw-r--r-- | net/atm/br2684.c | 91 | 
1 files changed, 71 insertions, 20 deletions
| diff --git a/net/atm/br2684.c b/net/atm/br2684.c index 4819d31533e0..403e71fa88fe 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -68,12 +68,15 @@ struct br2684_vcc {  	/* keep old push, pop functions for chaining */  	void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb);  	void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb); +	void (*old_release_cb)(struct atm_vcc *vcc); +	struct module *old_owner;  	enum br2684_encaps encaps;  	struct list_head brvccs;  #ifdef CONFIG_ATM_BR2684_IPFILTER  	struct br2684_filter filter;  #endif /* CONFIG_ATM_BR2684_IPFILTER */  	unsigned int copies_needed, copies_failed; +	atomic_t qspace;  };  struct br2684_dev { @@ -181,18 +184,15 @@ static struct notifier_block atm_dev_notifier = {  static void br2684_pop(struct atm_vcc *vcc, struct sk_buff *skb)  {  	struct br2684_vcc *brvcc = BR2684_VCC(vcc); -	struct net_device *net_dev = skb->dev; -	pr_debug("(vcc %p ; net_dev %p )\n", vcc, net_dev); +	pr_debug("(vcc %p ; net_dev %p )\n", vcc, brvcc->device);  	brvcc->old_pop(vcc, skb); -	if (!net_dev) -		return; - -	if (atm_may_send(vcc, 0)) -		netif_wake_queue(net_dev); - +	/* If the queue space just went up from zero, wake */ +	if (atomic_inc_return(&brvcc->qspace) == 1) +		netif_wake_queue(brvcc->device);  } +  /*   * Send a packet out a particular vcc.  Not to useful right now, but paves   * the way for multiple vcc's per itf.  Returns true if we can send, @@ -256,16 +256,30 @@ static int br2684_xmit_vcc(struct sk_buff *skb, struct net_device *dev,  	ATM_SKB(skb)->atm_options = atmvcc->atm_options;  	dev->stats.tx_packets++;  	dev->stats.tx_bytes += skb->len; -	atmvcc->send(atmvcc, skb); -	if (!atm_may_send(atmvcc, 0)) { +	if (atomic_dec_return(&brvcc->qspace) < 1) { +		/* No more please! */  		netif_stop_queue(brvcc->device); -		/*check for race with br2684_pop*/ -		if (atm_may_send(atmvcc, 0)) -			netif_start_queue(brvcc->device); +		/* We might have raced with br2684_pop() */ +		if (unlikely(atomic_read(&brvcc->qspace) > 0)) +			netif_wake_queue(brvcc->device);  	} -	return 1; +	/* If this fails immediately, the skb will be freed and br2684_pop() +	   will wake the queue if appropriate. Just return an error so that +	   the stats are updated correctly */ +	return !atmvcc->send(atmvcc, skb); +} + +static void br2684_release_cb(struct atm_vcc *atmvcc) +{ +	struct br2684_vcc *brvcc = BR2684_VCC(atmvcc); + +	if (atomic_read(&brvcc->qspace) > 0) +		netif_wake_queue(brvcc->device); + +	if (brvcc->old_release_cb) +		brvcc->old_release_cb(atmvcc);  }  static inline struct br2684_vcc *pick_outgoing_vcc(const struct sk_buff *skb, @@ -279,6 +293,8 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb,  {  	struct br2684_dev *brdev = BRPRIV(dev);  	struct br2684_vcc *brvcc; +	struct atm_vcc *atmvcc; +	netdev_tx_t ret = NETDEV_TX_OK;  	pr_debug("skb_dst(skb)=%p\n", skb_dst(skb));  	read_lock(&devs_lock); @@ -289,9 +305,26 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb,  		dev->stats.tx_carrier_errors++;  		/* netif_stop_queue(dev); */  		dev_kfree_skb(skb); -		read_unlock(&devs_lock); -		return NETDEV_TX_OK; +		goto out_devs;  	} +	atmvcc = brvcc->atmvcc; + +	bh_lock_sock(sk_atm(atmvcc)); + +	if (test_bit(ATM_VF_RELEASED, &atmvcc->flags) || +	    test_bit(ATM_VF_CLOSE, &atmvcc->flags) || +	    !test_bit(ATM_VF_READY, &atmvcc->flags)) { +		dev->stats.tx_dropped++; +		dev_kfree_skb(skb); +		goto out; +	} + +	if (sock_owned_by_user(sk_atm(atmvcc))) { +		netif_stop_queue(brvcc->device); +		ret = NETDEV_TX_BUSY; +		goto out; +	} +  	if (!br2684_xmit_vcc(skb, dev, brvcc)) {  		/*  		 * We should probably use netif_*_queue() here, but that @@ -303,8 +336,11 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb,  		dev->stats.tx_errors++;  		dev->stats.tx_fifo_errors++;  	} + out: +	bh_unlock_sock(sk_atm(atmvcc)); + out_devs:  	read_unlock(&devs_lock); -	return NETDEV_TX_OK; +	return ret;  }  /* @@ -377,9 +413,10 @@ static void br2684_close_vcc(struct br2684_vcc *brvcc)  	list_del(&brvcc->brvccs);  	write_unlock_irq(&devs_lock);  	brvcc->atmvcc->user_back = NULL;	/* what about vcc->recvq ??? */ +	brvcc->atmvcc->release_cb = brvcc->old_release_cb;  	brvcc->old_push(brvcc->atmvcc, NULL);	/* pass on the bad news */ +	module_put(brvcc->old_owner);  	kfree(brvcc); -	module_put(THIS_MODULE);  }  /* when AAL5 PDU comes in: */ @@ -504,6 +541,13 @@ static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg)  	brvcc = kzalloc(sizeof(struct br2684_vcc), GFP_KERNEL);  	if (!brvcc)  		return -ENOMEM; +	/* +	 * Allow two packets in the ATM queue. One actually being sent, and one +	 * for the ATM 'TX done' handler to send. It shouldn't take long to get +	 * the next one from the netdev queue, when we need it. More than that +	 * would be bufferbloat. +	 */ +	atomic_set(&brvcc->qspace, 2);  	write_lock_irq(&devs_lock);  	net_dev = br2684_find_dev(&be.ifspec);  	if (net_dev == NULL) { @@ -546,9 +590,13 @@ static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg)  	brvcc->encaps = (enum br2684_encaps)be.encaps;  	brvcc->old_push = atmvcc->push;  	brvcc->old_pop = atmvcc->pop; +	brvcc->old_release_cb = atmvcc->release_cb; +	brvcc->old_owner = atmvcc->owner;  	barrier();  	atmvcc->push = br2684_push;  	atmvcc->pop = br2684_pop; +	atmvcc->release_cb = br2684_release_cb; +	atmvcc->owner = THIS_MODULE;  	/* initialize netdev carrier state */  	if (atmvcc->dev->signal == ATM_PHY_SIG_LOST) @@ -687,10 +735,13 @@ static int br2684_ioctl(struct socket *sock, unsigned int cmd,  			return -ENOIOCTLCMD;  		if (!capable(CAP_NET_ADMIN))  			return -EPERM; -		if (cmd == ATM_SETBACKEND) +		if (cmd == ATM_SETBACKEND) { +			if (sock->state != SS_CONNECTED) +				return -EINVAL;  			return br2684_regvcc(atmvcc, argp); -		else +		} else {  			return br2684_create(argp); +		}  #ifdef CONFIG_ATM_BR2684_IPFILTER  	case BR2684_SETFILT:  		if (atmvcc->push != br2684_push) |