diff options
Diffstat (limited to 'drivers/net/wan/lapbether.c')
| -rw-r--r-- | drivers/net/wan/lapbether.c | 85 | 
1 files changed, 71 insertions, 14 deletions
| diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c index c3372498f4f1..59646865a3a4 100644 --- a/drivers/net/wan/lapbether.c +++ b/drivers/net/wan/lapbether.c @@ -51,6 +51,10 @@ struct lapbethdev {  	struct list_head	node;  	struct net_device	*ethdev;	/* link to ethernet device */  	struct net_device	*axdev;		/* lapbeth device (lapb#) */ +	bool			up; +	spinlock_t		up_lock;	/* Protects "up" */ +	struct sk_buff_head	rx_queue; +	struct napi_struct	napi;  };  static LIST_HEAD(lapbeth_devices); @@ -81,6 +85,26 @@ static __inline__ int dev_is_ethdev(struct net_device *dev)  /* ------------------------------------------------------------------------ */ +static int lapbeth_napi_poll(struct napi_struct *napi, int budget) +{ +	struct lapbethdev *lapbeth = container_of(napi, struct lapbethdev, +						  napi); +	struct sk_buff *skb; +	int processed = 0; + +	for (; processed < budget; ++processed) { +		skb = skb_dequeue(&lapbeth->rx_queue); +		if (!skb) +			break; +		netif_receive_skb_core(skb); +	} + +	if (processed < budget) +		napi_complete(napi); + +	return processed; +} +  /*   *	Receive a LAPB frame via an ethernet interface.   */ @@ -101,8 +125,9 @@ static int lapbeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packe  	rcu_read_lock();  	lapbeth = lapbeth_get_x25_dev(dev);  	if (!lapbeth) -		goto drop_unlock; -	if (!netif_running(lapbeth->axdev)) +		goto drop_unlock_rcu; +	spin_lock_bh(&lapbeth->up_lock); +	if (!lapbeth->up)  		goto drop_unlock;  	len = skb->data[0] + skb->data[1] * 256; @@ -117,11 +142,14 @@ static int lapbeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packe  		goto drop_unlock;  	}  out: +	spin_unlock_bh(&lapbeth->up_lock);  	rcu_read_unlock();  	return 0;  drop_unlock:  	kfree_skb(skb);  	goto out; +drop_unlock_rcu: +	rcu_read_unlock();  drop:  	kfree_skb(skb);  	return 0; @@ -129,6 +157,7 @@ drop:  static int lapbeth_data_indication(struct net_device *dev, struct sk_buff *skb)  { +	struct lapbethdev *lapbeth = netdev_priv(dev);  	unsigned char *ptr;  	if (skb_cow(skb, 1)) { @@ -142,7 +171,10 @@ static int lapbeth_data_indication(struct net_device *dev, struct sk_buff *skb)  	*ptr = X25_IFACE_DATA;  	skb->protocol = x25_type_trans(skb, dev); -	return netif_rx(skb); + +	skb_queue_tail(&lapbeth->rx_queue, skb); +	napi_schedule(&lapbeth->napi); +	return NET_RX_SUCCESS;  }  /* @@ -151,13 +183,11 @@ static int lapbeth_data_indication(struct net_device *dev, struct sk_buff *skb)  static netdev_tx_t lapbeth_xmit(struct sk_buff *skb,  				      struct net_device *dev)  { +	struct lapbethdev *lapbeth = netdev_priv(dev);  	int err; -	/* -	 * Just to be *really* sure not to send anything if the interface -	 * is down, the ethernet device may have gone. -	 */ -	if (!netif_running(dev)) +	spin_lock_bh(&lapbeth->up_lock); +	if (!lapbeth->up)  		goto drop;  	/* There should be a pseudo header of 1 byte added by upper layers. @@ -194,6 +224,7 @@ static netdev_tx_t lapbeth_xmit(struct sk_buff *skb,  		goto drop;  	}  out: +	spin_unlock_bh(&lapbeth->up_lock);  	return NETDEV_TX_OK;  drop:  	kfree_skb(skb); @@ -228,8 +259,9 @@ static void lapbeth_data_transmit(struct net_device *ndev, struct sk_buff *skb)  static void lapbeth_connected(struct net_device *dev, int reason)  { +	struct lapbethdev *lapbeth = netdev_priv(dev);  	unsigned char *ptr; -	struct sk_buff *skb = dev_alloc_skb(1); +	struct sk_buff *skb = __dev_alloc_skb(1, GFP_ATOMIC | __GFP_NOMEMALLOC);  	if (!skb) {  		pr_err("out of memory\n"); @@ -240,13 +272,16 @@ static void lapbeth_connected(struct net_device *dev, int reason)  	*ptr = X25_IFACE_CONNECT;  	skb->protocol = x25_type_trans(skb, dev); -	netif_rx(skb); + +	skb_queue_tail(&lapbeth->rx_queue, skb); +	napi_schedule(&lapbeth->napi);  }  static void lapbeth_disconnected(struct net_device *dev, int reason)  { +	struct lapbethdev *lapbeth = netdev_priv(dev);  	unsigned char *ptr; -	struct sk_buff *skb = dev_alloc_skb(1); +	struct sk_buff *skb = __dev_alloc_skb(1, GFP_ATOMIC | __GFP_NOMEMALLOC);  	if (!skb) {  		pr_err("out of memory\n"); @@ -257,7 +292,9 @@ static void lapbeth_disconnected(struct net_device *dev, int reason)  	*ptr = X25_IFACE_DISCONNECT;  	skb->protocol = x25_type_trans(skb, dev); -	netif_rx(skb); + +	skb_queue_tail(&lapbeth->rx_queue, skb); +	napi_schedule(&lapbeth->napi);  }  /* @@ -285,23 +322,37 @@ static const struct lapb_register_struct lapbeth_callbacks = {   */  static int lapbeth_open(struct net_device *dev)  { +	struct lapbethdev *lapbeth = netdev_priv(dev);  	int err; +	napi_enable(&lapbeth->napi); +  	if ((err = lapb_register(dev, &lapbeth_callbacks)) != LAPB_OK) {  		pr_err("lapb_register error: %d\n", err);  		return -ENODEV;  	} +	spin_lock_bh(&lapbeth->up_lock); +	lapbeth->up = true; +	spin_unlock_bh(&lapbeth->up_lock); +  	return 0;  }  static int lapbeth_close(struct net_device *dev)  { +	struct lapbethdev *lapbeth = netdev_priv(dev);  	int err; +	spin_lock_bh(&lapbeth->up_lock); +	lapbeth->up = false; +	spin_unlock_bh(&lapbeth->up_lock); +  	if ((err = lapb_unregister(dev)) != LAPB_OK)  		pr_err("lapb_unregister error: %d\n", err); +	napi_disable(&lapbeth->napi); +  	return 0;  } @@ -356,6 +407,12 @@ static int lapbeth_new_device(struct net_device *dev)  	dev_hold(dev);  	lapbeth->ethdev = dev; +	lapbeth->up = false; +	spin_lock_init(&lapbeth->up_lock); + +	skb_queue_head_init(&lapbeth->rx_queue); +	netif_napi_add(ndev, &lapbeth->napi, lapbeth_napi_poll, 16); +  	rc = -EIO;  	if (register_netdevice(ndev))  		goto fail; @@ -403,8 +460,8 @@ static int lapbeth_device_event(struct notifier_block *this,  		if (lapbeth_get_x25_dev(dev) == NULL)  			lapbeth_new_device(dev);  		break; -	case NETDEV_DOWN:	 -		/* ethernet device closed -> close LAPB interface */ +	case NETDEV_GOING_DOWN: +		/* ethernet device closes -> close LAPB interface */  		lapbeth = lapbeth_get_x25_dev(dev);  		if (lapbeth)   			dev_close(lapbeth->axdev); | 
