Skip to content

Commit

Permalink
netdev2_tap: continue reading even if no spaces left in pktbuf
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Yonezawa-T2 committed Mar 31, 2016
1 parent 79d3389 commit c4bc424
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 22 deletions.
70 changes: 48 additions & 22 deletions cpu/native/netdev2_tap/netdev2_tap.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,58 @@ static inline bool _is_addr_multicast(uint8_t *addr)
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)
{
netdev2_tap_t *dev = (netdev2_tap_t*)netdev2;
(void)info;

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,
* so we return the maximum possible size */
return ETHERNET_FRAME_LEN;
Expand All @@ -212,28 +258,8 @@ static int _recv(netdev2_t *netdev2, char *buf, int len, void *info)

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 */

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--;
_continue_reading(dev);

#ifdef MODULE_NETSTATS_L2
netdev2->stats.rx_count++;
Expand Down Expand Up @@ -317,7 +343,7 @@ static int _init(netdev2_t *netdev)
/* initialize device descriptor */
dev->promiscous = 0;
/* 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);
}
#if (defined(__MACH__) || defined(__FreeBSD__)) /* OSX/FreeBSD */
Expand Down
3 changes: 3 additions & 0 deletions drivers/include/net/netdev2.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ typedef struct netdev2_driver {
*
* 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[out] buf buffer to write into or NULL
* @param[in] len maximum nr. of bytes to read
Expand Down
4 changes: 4 additions & 0 deletions sys/net/gnrc/link_layer/netdev2/gnrc_netdev2_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ static gnrc_pktsnip_t *_recv(gnrc_netdev2_t *gnrc_netdev2)

if(!pkt) {
DEBUG("_recv_ethernet_packet: cannot allocate pktsnip.\n");

/* drop the packet */
dev->driver->recv(dev, NULL, bytes_expected, NULL);

goto out;
}

Expand Down

0 comments on commit c4bc424

Please sign in to comment.