summaryrefslogtreecommitdiffstats
path: root/drivers/net/wan/lapbether.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wan/lapbether.c')
-rw-r--r--drivers/net/wan/lapbether.c85
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);