netdev2_tap: continue reading even if no spaces left in pktbuf
On OS X, `netdev2_tap` suspends monitoring file descriptor until `_recv` is called. If no spaces in left in pktbuf, `gnrc_netdev2_eth` does not call `_recv` that results in deadlock. With this commit, `gnrc_netdev2_eth` calls `_recv` with NULL buffer and non-zero length parameter, that indicates the driver to drop frame and resume working.
This commit is contained in:
parent
79d33897cb
commit
c4bc42419b
@ -184,12 +184,58 @@ static inline bool _is_addr_multicast(uint8_t *addr)
|
|||||||
return (addr[0] & 0x01);
|
return (addr[0] & 0x01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _continue_reading(netdev2_tap_t *dev)
|
||||||
|
{
|
||||||
|
/* work around lost signals */
|
||||||
|
fd_set rfds;
|
||||||
|
struct timeval t;
|
||||||
|
memset(&t, 0, sizeof(t));
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(dev->tap_fd, &rfds);
|
||||||
|
|
||||||
|
_native_in_syscall++; /* no switching here */
|
||||||
|
|
||||||
|
if (real_select(dev->tap_fd + 1, &rfds, NULL, NULL, &t) == 1) {
|
||||||
|
int sig = SIGIO;
|
||||||
|
extern int _sig_pipefd[2];
|
||||||
|
extern ssize_t (*real_write)(int fd, const void * buf, size_t count);
|
||||||
|
real_write(_sig_pipefd[1], &sig, sizeof(int));
|
||||||
|
_native_sigpend++;
|
||||||
|
DEBUG("netdev2_tap: sigpend++\n");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DEBUG("netdev2_tap: native_async_read_continue\n");
|
||||||
|
native_async_read_continue(dev->tap_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
_native_in_syscall--;
|
||||||
|
}
|
||||||
|
|
||||||
static int _recv(netdev2_t *netdev2, char *buf, int len, void *info)
|
static int _recv(netdev2_t *netdev2, char *buf, int len, void *info)
|
||||||
{
|
{
|
||||||
netdev2_tap_t *dev = (netdev2_tap_t*)netdev2;
|
netdev2_tap_t *dev = (netdev2_tap_t*)netdev2;
|
||||||
(void)info;
|
(void)info;
|
||||||
|
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
|
if (len > 0) {
|
||||||
|
/* no memory available in pktbuf, discarding the frame */
|
||||||
|
DEBUG("netdev2_tap: discarding the frame\n");
|
||||||
|
|
||||||
|
/* repeating `real_read` for small size on tap device results in
|
||||||
|
* freeze for some reason. Using a large buffer for now. */
|
||||||
|
/*
|
||||||
|
uint8_t buf[4];
|
||||||
|
while (real_read(dev->tap_fd, buf, sizeof(buf)) > 0) {
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static uint8_t buf[ETHERNET_FRAME_LEN];
|
||||||
|
|
||||||
|
real_read(dev->tap_fd, buf, sizeof(buf));
|
||||||
|
|
||||||
|
_continue_reading(dev);
|
||||||
|
}
|
||||||
|
|
||||||
/* no way of figuring out packet size without racey buffering,
|
/* no way of figuring out packet size without racey buffering,
|
||||||
* so we return the maximum possible size */
|
* so we return the maximum possible size */
|
||||||
return ETHERNET_FRAME_LEN;
|
return ETHERNET_FRAME_LEN;
|
||||||
@ -212,28 +258,8 @@ static int _recv(netdev2_t *netdev2, char *buf, int len, void *info)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* work around lost signals */
|
|
||||||
fd_set rfds;
|
|
||||||
struct timeval t;
|
|
||||||
memset(&t, 0, sizeof(t));
|
|
||||||
FD_ZERO(&rfds);
|
|
||||||
FD_SET(dev->tap_fd, &rfds);
|
|
||||||
|
|
||||||
_native_in_syscall++; /* no switching here */
|
_continue_reading(dev);
|
||||||
|
|
||||||
if (real_select(dev->tap_fd + 1, &rfds, NULL, NULL, &t) == 1) {
|
|
||||||
int sig = SIGIO;
|
|
||||||
extern int _sig_pipefd[2];
|
|
||||||
extern ssize_t (*real_write)(int fd, const void * buf, size_t count);
|
|
||||||
real_write(_sig_pipefd[1], &sig, sizeof(int));
|
|
||||||
_native_sigpend++;
|
|
||||||
DEBUG("netdev2_tap: sigpend++\n");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
native_async_read_continue(dev->tap_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
_native_in_syscall--;
|
|
||||||
|
|
||||||
#ifdef MODULE_NETSTATS_L2
|
#ifdef MODULE_NETSTATS_L2
|
||||||
netdev2->stats.rx_count++;
|
netdev2->stats.rx_count++;
|
||||||
@ -317,7 +343,7 @@ static int _init(netdev2_t *netdev)
|
|||||||
/* initialize device descriptor */
|
/* initialize device descriptor */
|
||||||
dev->promiscous = 0;
|
dev->promiscous = 0;
|
||||||
/* implicitly create the tap interface */
|
/* implicitly create the tap interface */
|
||||||
if ((dev->tap_fd = real_open(clonedev , O_RDWR)) == -1) {
|
if ((dev->tap_fd = real_open(clonedev, O_RDWR | O_NONBLOCK)) == -1) {
|
||||||
err(EXIT_FAILURE, "open(%s)", clonedev);
|
err(EXIT_FAILURE, "open(%s)", clonedev);
|
||||||
}
|
}
|
||||||
#if (defined(__MACH__) || defined(__FreeBSD__)) /* OSX/FreeBSD */
|
#if (defined(__MACH__) || defined(__FreeBSD__)) /* OSX/FreeBSD */
|
||||||
|
|||||||
@ -135,6 +135,9 @@ typedef struct netdev2_driver {
|
|||||||
*
|
*
|
||||||
* Supposed to be called from netdev2_event_handler().
|
* Supposed to be called from netdev2_event_handler().
|
||||||
*
|
*
|
||||||
|
* If buf == NULL and len == 0, returns the packet size without dropping it.
|
||||||
|
* If buf == NULL and len > 0, drops the packet and returns the packet size.
|
||||||
|
*
|
||||||
* @param[in] dev network device descriptor
|
* @param[in] dev network device descriptor
|
||||||
* @param[out] buf buffer to write into or NULL
|
* @param[out] buf buffer to write into or NULL
|
||||||
* @param[in] len maximum nr. of bytes to read
|
* @param[in] len maximum nr. of bytes to read
|
||||||
|
|||||||
@ -42,6 +42,10 @@ static gnrc_pktsnip_t *_recv(gnrc_netdev2_t *gnrc_netdev2)
|
|||||||
|
|
||||||
if(!pkt) {
|
if(!pkt) {
|
||||||
DEBUG("_recv_ethernet_packet: cannot allocate pktsnip.\n");
|
DEBUG("_recv_ethernet_packet: cannot allocate pktsnip.\n");
|
||||||
|
|
||||||
|
/* drop the packet */
|
||||||
|
dev->driver->recv(dev, NULL, bytes_expected, NULL);
|
||||||
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user