Improve tun (#198)

* Remove the code duplication in tun_net_receive_tap and remove the unused filep field
  * Shouldn't return -EBUSY in tun_write.  Let the caller wait until the write buffer free
  * Handle that write buffer is ready first correctly in tun_read
  * Remove the unused tun_ipv6multicast
This commit is contained in:
Xiang Xiao 2020-02-02 08:02:17 -06:00 committed by Gregory Nutt
parent 5c80b94820
commit e9bc32cdbc

View File

@ -134,12 +134,13 @@ struct tun_device_s
{
bool bifup; /* true:ifup false:ifdown */
bool read_wait;
bool write_wait;
WDOG_ID txpoll; /* TX poll timer */
struct work_s work; /* For deferring poll work to the work queue */
FAR struct file *filep;
FAR struct pollfd *poll_fds;
sem_t waitsem;
sem_t read_wait_sem;
sem_t write_wait_sem;
size_t read_d_len;
size_t write_d_len;
@ -170,7 +171,7 @@ static void tun_unlock(FAR struct tun_device_s *priv);
/* Common TX logic */
static int tun_fd_transmit(FAR struct tun_device_s *priv);
static void tun_fd_transmit(FAR struct tun_device_s *priv);
static int tun_txpoll(FAR struct net_driver_s *dev);
#ifdef CONFIG_NET_ETHERNET
static int tun_txpoll_tap(FAR struct net_driver_s *dev);
@ -201,13 +202,11 @@ static int tun_txavail(FAR struct net_driver_s *dev);
static int tun_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac);
static int tun_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac);
#endif
#ifdef CONFIG_NET_ICMPv6
static void tun_ipv6multicast(FAR struct tun_device_s *priv);
#endif
static int tun_dev_init(FAR struct tun_device_s *priv, FAR struct file *filep,
static int tun_dev_init(FAR struct tun_device_s *priv,
FAR struct file *filep,
FAR const char *devfmt, bool tun);
static int tun_dev_uninit(FAR struct tun_device_s *priv);
static void tun_dev_uninit(FAR struct tun_device_s *priv);
/* File interface */
@ -291,6 +290,18 @@ static void tun_pollnotify(FAR struct tun_device_s *priv,
{
FAR struct pollfd *fds = priv->poll_fds;
if (priv->read_wait && (eventset & POLLIN))
{
priv->read_wait = false;
nxsem_post(&priv->read_wait_sem);
}
if (priv->write_wait && (eventset & POLLOUT))
{
priv->write_wait = false;
nxsem_post(&priv->write_wait_sem);
}
if (fds == NULL)
{
return;
@ -316,7 +327,7 @@ static void tun_pollnotify(FAR struct tun_device_s *priv,
* priv - Reference to the driver state structure
*
* Returned Value:
* OK on success; a negated errno on failure
* None
*
* Assumptions:
* May or may not be called from an interrupt handler. In either case,
@ -325,23 +336,10 @@ static void tun_pollnotify(FAR struct tun_device_s *priv,
*
****************************************************************************/
static int tun_fd_transmit(FAR struct tun_device_s *priv)
static void tun_fd_transmit(FAR struct tun_device_s *priv)
{
NETDEV_TXPACKETS(&priv->dev);
/* Verify that the hardware is ready to send another packet. If we get
* here, then we are committed to sending a packet; Higher level logic
* must have assured that there is no transmission in progress.
*/
if (priv->read_wait)
{
priv->read_wait = false;
nxsem_post(&priv->read_wait_sem);
}
tun_pollnotify(priv, POLLIN);
return OK;
}
/****************************************************************************
@ -452,8 +450,8 @@ static int tun_txpoll_tap(FAR struct net_driver_s *dev)
}
}
/* If zero is returned, the polling will continue until all connections have
* been examined.
/* If zero is returned, the polling will continue until all connections
* have been examined.
*/
return 0;
@ -589,33 +587,6 @@ static void tun_net_receive_tap(FAR struct tun_device_s *priv)
arp_ipin(&priv->dev);
ipv4_input(&priv->dev);
/* If the above function invocation resulted in data that should be
* sent out on the network, the field d_len will set to a value > 0.
*/
if (priv->dev.d_len > 0)
{
/* Update the Ethernet header with the correct MAC address */
#ifdef CONFIG_NET_IPv6
if (IFF_IS_IPv4(priv->dev.d_flags))
#endif
{
arp_out(&priv->dev);
}
#ifdef CONFIG_NET_IPv6
else
{
neighbor_out(&priv->dev);
}
#endif
/* And send the packet */
priv->write_d_len = priv->dev.d_len;
tun_fd_transmit(priv);
}
}
else
#endif
@ -628,31 +599,6 @@ static void tun_net_receive_tap(FAR struct tun_device_s *priv)
/* Give the IPv6 packet to the network layer. */
ipv6_input(&priv->dev);
/* If the above function invocation resulted in data that should be
* sent out on the network, the field d_len will set to a value > 0.
*/
if (priv->dev.d_len > 0)
{
/* Update the Ethernet header with the correct MAC address */
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv4(priv->dev.d_flags))
{
arp_out(&priv->dev);
}
else
#endif
#ifdef CONFIG_NET_IPv6
{
neighbor_out(&priv->dev);
}
#endif
priv->write_d_len = priv->dev.d_len;
tun_fd_transmit(priv);
}
}
else
#endif
@ -670,12 +616,41 @@ static void tun_net_receive_tap(FAR struct tun_device_s *priv)
{
priv->write_d_len = priv->dev.d_len;
tun_fd_transmit(priv);
priv->dev.d_len = 0;
}
}
else
#endif
{
NETDEV_RXDROPPED(&priv->dev);
priv->dev.d_len = 0;
}
/* If the above function invocation resulted in data that should be
* sent out on the network, the field d_len will set to a value > 0.
*/
if (priv->dev.d_len > 0)
{
/* Update the Ethernet header with the correct MAC address */
#ifdef CONFIG_NET_IPv6
if (IFF_IS_IPv4(priv->dev.d_flags))
#endif
{
arp_out(&priv->dev);
}
#ifdef CONFIG_NET_IPv6
else
{
neighbor_out(&priv->dev);
}
#endif
/* And send the packet */
priv->write_d_len = priv->dev.d_len;
tun_fd_transmit(priv);
}
}
#endif
@ -889,16 +864,6 @@ static int tun_ifup(FAR struct net_driver_s *dev)
dev->d_ipv6addr[6], dev->d_ipv6addr[7]);
#endif
/* Initialize PHYs, the Ethernet interface, and setup up Ethernet interrupts */
/* Instantiate the MAC address from priv->dev.d_mac.ether.ether_addr_octet */
#ifdef CONFIG_NET_ICMPv6
/* Set up IPv6 multicast address filtering */
tun_ipv6multicast(priv);
#endif
/* Set and activate a timer process */
wd_start(priv->txpoll, TUN_WDDELAY, tun_poll_expiry,
@ -1113,7 +1078,8 @@ static void tun_ipv6multicast(FAR struct tun_device_s *priv)
*
****************************************************************************/
static int tun_dev_init(FAR struct tun_device_s *priv, FAR struct file *filep,
static int tun_dev_init(FAR struct tun_device_s *priv,
FAR struct file *filep,
FAR const char *devfmt, bool tun)
{
int ret;
@ -1134,16 +1100,18 @@ static int tun_dev_init(FAR struct tun_device_s *priv, FAR struct file *filep,
nxsem_init(&priv->waitsem, 0, 1);
nxsem_init(&priv->read_wait_sem, 0, 0);
nxsem_init(&priv->write_wait_sem, 0, 0);
/* The wait semaphore is used for signaling and, hence, should not have
* priority inheritance enabled.
*/
nxsem_setprotocol(&priv->read_wait_sem, SEM_PRIO_NONE);
nxsem_setprotocol(&priv->write_wait_sem, SEM_PRIO_NONE);
/* Create a watchdog for timing polling for and timing of transmissions */
priv->txpoll = wd_create(); /* Create periodic poll timer */
priv->txpoll = wd_create(); /* Create periodic poll timer */
/* Assign d_ifname if specified. */
@ -1159,12 +1127,11 @@ static int tun_dev_init(FAR struct tun_device_s *priv, FAR struct file *filep,
{
nxsem_destroy(&priv->waitsem);
nxsem_destroy(&priv->read_wait_sem);
nxsem_destroy(&priv->write_wait_sem);
return ret;
}
priv->filep = filep; /* Set link to file */
filep->f_priv = priv; /* Set link to TUN device */
filep->f_priv = priv; /* Set link to TUN device */
return ret;
}
@ -1172,7 +1139,7 @@ static int tun_dev_init(FAR struct tun_device_s *priv, FAR struct file *filep,
* Name: tun_dev_uninit
****************************************************************************/
static int tun_dev_uninit(FAR struct tun_device_s *priv)
static void tun_dev_uninit(FAR struct tun_device_s *priv)
{
/* Put the interface in the down state */
@ -1184,8 +1151,7 @@ static int tun_dev_uninit(FAR struct tun_device_s *priv)
nxsem_destroy(&priv->waitsem);
nxsem_destroy(&priv->read_wait_sem);
return OK;
nxsem_destroy(&priv->write_wait_sem);
}
/****************************************************************************
@ -1195,7 +1161,6 @@ static int tun_dev_uninit(FAR struct tun_device_s *priv)
static int tun_open(FAR struct file *filep)
{
filep->f_priv = 0;
return OK;
}
@ -1222,7 +1187,6 @@ static int tun_close(FAR struct file *filep)
tun_dev_uninit(priv);
tundev_unlock(tun);
return OK;
}
@ -1236,40 +1200,47 @@ static ssize_t tun_write(FAR struct file *filep, FAR const char *buffer,
FAR struct tun_device_s *priv = filep->f_priv;
ssize_t ret;
if (priv == NULL)
if (priv == NULL || buflen > CONFIG_NET_TUN_PKTSIZE)
{
return -EINVAL;
}
tun_lock(priv);
if (priv->write_d_len > 0)
for (; ; )
{
/* Check if there are free space to write */
if (priv->write_d_len == 0)
{
memcpy(priv->write_buf, buffer, buflen);
net_lock();
priv->dev.d_buf = priv->write_buf;
priv->dev.d_len = buflen;
tun_net_receive(priv);
net_unlock();
ret = buflen;
break;
}
/* Wait if there are no free space to write */
if ((filep->f_oflags & O_NONBLOCK) != 0)
{
ret = -EAGAIN;
break;
}
priv->write_wait = true;
tun_unlock(priv);
return -EBUSY;
nxsem_wait(&priv->write_wait_sem);
tun_lock(priv);
}
net_lock();
if (buflen > CONFIG_NET_TUN_PKTSIZE)
{
ret = -EINVAL;
}
else
{
memcpy(priv->write_buf, buffer, buflen);
priv->dev.d_buf = priv->write_buf;
priv->dev.d_len = buflen;
tun_net_receive(priv);
ret = (ssize_t)buflen;
}
net_unlock();
tun_unlock(priv);
return ret;
}
@ -1282,8 +1253,6 @@ static ssize_t tun_read(FAR struct file *filep, FAR char *buffer,
{
FAR struct tun_device_s *priv = filep->f_priv;
ssize_t ret;
size_t write_d_len;
size_t read_d_len;
if (priv == NULL)
{
@ -1292,33 +1261,53 @@ static ssize_t tun_read(FAR struct file *filep, FAR char *buffer,
tun_lock(priv);
/* Check if there are data to read in write buffer */
write_d_len = priv->write_d_len;
if (write_d_len > 0)
for (; ; )
{
if (buflen < write_d_len)
/* Check if there are data to read in write buffer */
if (priv->write_d_len > 0)
{
ret = -EINVAL;
goto out;
if (buflen < priv->write_d_len)
{
ret = -EINVAL;
break;
}
memcpy(buffer, priv->write_buf, priv->write_d_len);
ret = priv->write_d_len;
priv->write_d_len = 0;
NETDEV_TXDONE(&priv->dev);
tun_pollnotify(priv, POLLOUT);
break;
}
memcpy(buffer, priv->write_buf, write_d_len);
ret = (ssize_t)write_d_len;
/* Check if there are data to read in read buffer */
priv->write_d_len = 0;
NETDEV_TXDONE(&priv->dev);
tun_pollnotify(priv, POLLOUT);
if (priv->read_d_len > 0)
{
if (buflen < priv->read_d_len)
{
ret = -EINVAL;
break;
}
goto out;
}
memcpy(buffer, priv->read_buf, priv->read_d_len);
ret = priv->read_d_len;
priv->read_d_len = 0;
net_lock();
tun_txdone(priv);
net_unlock();
break;
}
/* Wait if there are no data to read */
if (priv->read_d_len == 0)
{
if ((filep->f_oflags & O_NONBLOCK) != 0)
{
ret = -EAGAIN;
goto out;
break;
}
priv->read_wait = true;
@ -1327,27 +1316,7 @@ static ssize_t tun_read(FAR struct file *filep, FAR char *buffer,
tun_lock(priv);
}
net_lock();
read_d_len = priv->read_d_len;
if (buflen < read_d_len)
{
ret = -EINVAL;
}
else
{
memcpy(buffer, priv->read_buf, read_d_len);
ret = (ssize_t)read_d_len;
}
priv->read_d_len = 0;
tun_txdone(priv);
net_unlock();
out:
tun_unlock(priv);
return ret;
}
@ -1410,7 +1379,6 @@ int tun_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup)
errout:
tun_unlock(priv);
return ret;
}