diff --git a/src/include/86box/network.h b/src/include/86box/network.h index 2fe0553f8..54181ae8e 100644 --- a/src/include/86box/network.h +++ b/src/include/86box/network.h @@ -56,7 +56,9 @@ #define NET_TYPE_PCAP 2 /* use the (Win)Pcap API */ #define NET_MAX_FRAME 1518 -#define NET_QUEUE_LEN 8 +/* Queue size must be a power of 2 */ +#define NET_QUEUE_LEN 16 +#define NET_QUEUE_LEN_MASK (NET_QUEUE_LEN - 1) #define NET_CARD_MAX 4 #define NET_HOST_INTF_MAX 64 @@ -92,12 +94,10 @@ typedef int (*NETSETLINKSTATE)(void *); typedef struct netpkt { uint8_t *data; int len; - uint64_t tsc; } netpkt_t; typedef struct { netpkt_t packets[NET_QUEUE_LEN]; - int size; int head; int tail; } netqueue_t; @@ -165,7 +165,9 @@ extern int network_card_get_from_internal_name(char *); extern const device_t *network_card_getdevice(int); extern int network_tx_pop(netcard_t *card, netpkt_t *out_pkt); +extern int network_tx_popv(netcard_t *card, netpkt_t *pkt_vec, int vec_size); extern int network_rx_put(netcard_t *card, uint8_t *bufp, int len); +extern int network_rx_put_pkt(netcard_t *card, netpkt_t *pkt); #ifdef __cplusplus } #endif diff --git a/src/network/net_pcap.c b/src/network/net_pcap.c index 0baa16186..9aa486316 100644 --- a/src/network/net_pcap.c +++ b/src/network/net_pcap.c @@ -72,6 +72,8 @@ #include <86box/network.h> #include <86box/net_event.h> +#define PCAP_PKT_BATCH NET_QUEUE_LEN + enum { NET_EVENT_STOP = 0, NET_EVENT_TX, @@ -120,6 +122,17 @@ struct pcap_if { void *addresses; bpf_u_int32 flags; }; + +struct pcap_send_queue { + u_int maxlen; /* Maximum size of the queue, in bytes. This + variable contains the size of the buffer field. */ + u_int len; /* Current size of the queue, in bytes. */ + char *buffer; /* Buffer containing the packets to be sent. */ +}; + +typedef struct pcap_send_queue pcap_send_queue; + +typedef void (*pcap_handler)(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes); #endif typedef struct { @@ -129,7 +142,11 @@ typedef struct { net_evt_t tx_event; net_evt_t stop_event; netpkt_t pkt; + netpkt_t pktv[PCAP_PKT_BATCH]; uint8_t mac_addr[6]; +#ifdef _WIN32 + struct pcap_send_queue *pcap_queue; +#endif } net_pcap_t; typedef struct { @@ -154,11 +171,16 @@ static int (*f_pcap_setnonblock)(void*, int, char*); static int (*f_pcap_set_immediate_mode)(void *, int); static int (*f_pcap_set_promisc)(void *, int); static int (*f_pcap_set_snaplen)(void *, int); +static int (*f_pcap_dispatch)(void *, int, pcap_handler callback, u_char *user); static void *(*f_pcap_create)(const char *, char*); static int (*f_pcap_activate)(void *); static void *(*f_pcap_geterr)(void *); #ifdef _WIN32 static HANDLE (*f_pcap_getevent)(void *); +static int (*f_pcap_sendqueue_queue)(void *, void *, void *); +static u_int (*f_pcap_sendqueue_transmit)(void *, void *, int sync); +static void *(*f_pcap_sendqueue_alloc)(u_int memsize); +static void (*f_pcap_sendqueue_destroy)(void *); #else static int (*f_pcap_get_selectable_fd)(void *); #endif @@ -177,13 +199,18 @@ static dllimp_t pcap_imports[] = { { "pcap_set_immediate_mode", &f_pcap_set_immediate_mode}, { "pcap_set_promisc", &f_pcap_set_promisc }, { "pcap_set_snaplen", &f_pcap_set_snaplen }, + { "pcap_dispatch", &f_pcap_dispatch }, { "pcap_create", &f_pcap_create }, { "pcap_activate", &f_pcap_activate }, { "pcap_geterr", &f_pcap_geterr }, #ifdef _WIN32 { "pcap_getevent", &f_pcap_getevent }, + { "pcap_sendqueue_queue", &f_pcap_sendqueue_queue }, + { "pcap_sendqueue_transmit", &f_pcap_sendqueue_transmit}, + { "pcap_sendqueue_alloc", &f_pcap_sendqueue_alloc }, + { "pcap_sendqueue_destroy", &f_pcap_sendqueue_destroy }, #else - { "pcap_get_selectable_fd", &f_pcap_get_selectable_fd }, + { "pcap_get_selectable_fd", &f_pcap_get_selectable_fd }, #endif { NULL, NULL }, }; @@ -208,15 +235,12 @@ pcap_log(const char *fmt, ...) static void -net_pcap_read_packet(net_pcap_t *pcap) +net_pcap_rx_handler(uint8_t *user, const struct pcap_pkthdr *h, const uint8_t *bytes) { - struct pcap_pkthdr h; - - uint8_t *data = (uint8_t *) f_pcap_next((void *) pcap->pcap, &h); - if (!data) - return; - - network_rx_put(pcap->card, data, h.caplen); + net_pcap_t *pcap = (net_pcap_t*)user; + memcpy(pcap->pkt.data, bytes, h->caplen); + pcap->pkt.len = h->caplen; + network_rx_put_pkt(pcap->card, &pcap->pkt); } /* Send a packet to the Pcap interface. */ @@ -251,6 +275,7 @@ net_pcap_thread(void *priv) bool run = true; + struct pcap_pkthdr h; while (run) { int ret = WaitForMultipleObjects(NET_EVENT_MAX, events, FALSE, INFINITE); @@ -262,13 +287,17 @@ net_pcap_thread(void *priv) case NET_EVENT_TX: net_event_clear(&pcap->tx_event); - while (network_tx_pop(pcap->card, &pcap->pkt)) { - net_pcap_in(pcap->pcap, pcap->pkt.data, pcap->pkt.len); + int packets = network_tx_popv(pcap->card, pcap->pktv, PCAP_PKT_BATCH); + for (int i = 0; i < packets; i++) { + h.caplen = pcap->pktv[i].len; + f_pcap_sendqueue_queue(pcap->pcap_queue, &h, pcap->pktv[i].data); } + f_pcap_sendqueue_transmit(pcap->pcap, pcap->pcap_queue, 0); + pcap->pcap_queue->len = 0; break; case NET_EVENT_RX: - net_pcap_read_packet(pcap); + f_pcap_dispatch(pcap->pcap, PCAP_PKT_BATCH, net_pcap_rx_handler, (u_char *)pcap); break; } } @@ -305,13 +334,14 @@ net_pcap_thread(void *priv) if (pfd[NET_EVENT_TX].revents & POLLIN) { net_event_clear(&pcap->tx_event); - if (network_tx_pop(pcap->card, &pcap->pkt)) { - net_pcap_in(pcap->pcap, pcap->pkt.data, pcap->pkt.len); + int packets = network_tx_popv(pcap->card, pcap->pktv, PCAP_PKT_BATCH); + for (int i = 0; i < packets; i++) { + net_pcap_in(pcap->pcap, pcap->pktv[i].data, pcap->pktv[i].len); } } if (pfd[NET_EVENT_RX].revents & POLLIN) { - net_pcap_read_packet(pcap); + f_pcap_dispatch(pcap->pcap, PCAP_PKT_BATCH, net_pcap_rx_handler, (u_char *)pcap); } } @@ -470,7 +500,15 @@ net_pcap_init(const netcard_t *card, const uint8_t *mac_addr, void *priv) return NULL; } +#ifdef _WIN32 + pcap->pcap_queue = f_pcap_sendqueue_alloc(PCAP_PKT_BATCH * NET_MAX_FRAME); +#endif + + for (int i = 0; i < PCAP_PKT_BATCH; i++) { + pcap->pktv[i].data = calloc(1, NET_MAX_FRAME); + } pcap->pkt.data = calloc(1, NET_MAX_FRAME); + net_event_init(&pcap->tx_event); net_event_init(&pcap->stop_event); pcap->poll_tid = thread_create(net_pcap_thread, pcap); @@ -497,8 +535,14 @@ net_pcap_close(void *priv) thread_wait(pcap->poll_tid); pcap_log("PCAP: thread ended\n"); + for (int i = 0; i < PCAP_PKT_BATCH; i++) { + free(pcap->pktv[i].data); + } free(pcap->pkt.data); +#ifdef _WIN32 + f_pcap_sendqueue_destroy((void*)pcap->pcap_queue); +#endif /* OK, now shut down Pcap itself. */ f_pcap_close((void*)pcap->pcap); diff --git a/src/network/net_slirp.c b/src/network/net_slirp.c index 7535a0609..46b913416 100644 --- a/src/network/net_slirp.c +++ b/src/network/net_slirp.c @@ -45,6 +45,8 @@ #endif #include <86box/net_event.h> +#define SLIRP_PKT_BATCH NET_QUEUE_LEN + enum { NET_EVENT_STOP = 0, NET_EVENT_TX, @@ -60,6 +62,7 @@ typedef struct { net_evt_t tx_event; net_evt_t stop_event; netpkt_t pkt; + netpkt_t pkt_tx_v[SLIRP_PKT_BATCH]; #ifdef _WIN32 HANDLE sock_event; #else @@ -153,22 +156,12 @@ ssize_t net_slirp_send_packet(const void *qp, size_t pkt_len, void *opaque) { net_slirp_t *slirp = (net_slirp_t *) opaque; - uint8_t *mac = slirp->mac_addr; - uint32_t mac_cmp32[2]; - uint16_t mac_cmp16[2]; slirp_log("SLiRP: received %d-byte packet\n", pkt_len); - /* Received MAC. */ - mac_cmp32[0] = *(uint32_t *) (((uint8_t *) qp) + 6); - mac_cmp16[0] = *(uint16_t *) (((uint8_t *) qp) + 10); - - /* Local MAC. */ - mac_cmp32[1] = *(uint32_t *) mac; - mac_cmp16[1] = *(uint16_t *) (mac + 4); - if ((mac_cmp32[0] != mac_cmp32[1]) || (mac_cmp16[0] != mac_cmp16[1])) { - network_rx_put(slirp->card, (uint8_t *) qp, pkt_len); - } + memcpy(slirp->pkt.data, (uint8_t*) qp, pkt_len); + slirp->pkt.len = pkt_len; + network_rx_put_pkt(slirp->card, &slirp->pkt); return pkt_len; } @@ -336,8 +329,9 @@ net_slirp_thread(void *priv) break; case NET_EVENT_TX: - while (network_tx_pop(slirp->card, &slirp->pkt)) { - net_slirp_in(slirp, slirp->pkt.data, slirp->pkt.len); + int packets = network_tx_popv(slirp->card, slirp->pkt_tx_v, SLIRP_PKT_BATCH); + for (int i = 0; i < packets; i++) { + net_slirp_in(slirp, slirp->pkt_tx_v[i].data, slirp->pkt_tx_v[i].len); } break; @@ -381,11 +375,11 @@ net_slirp_thread(void *priv) if (slirp->pfd[NET_EVENT_TX].revents & POLLIN) { net_event_clear(&slirp->tx_event); - if (network_tx_pop(slirp->card, &slirp->pkt)) { - net_slirp_in(slirp, slirp->pkt.data, slirp->pkt.len); + int packets = network_tx_popv(slirp->card, slirp->pkt_tx_v, SLIRP_PKT_BATCH); + for (int i = 0; i < packets; i++) { + net_slirp_in(slirp, slirp->pkt_tx_v[i].data, slirp->pkt_tx_v[i].len); } } - } slirp_log("SLiRP: polling stopped.\n"); @@ -453,6 +447,9 @@ net_slirp_init(const netcard_t *card, const uint8_t *mac_addr, void *priv) i++; } + for (int i = 0; i < SLIRP_PKT_BATCH; i++) { + slirp->pkt_tx_v[i].data = calloc(1, NET_MAX_FRAME); + } slirp->pkt.data = calloc(1, NET_MAX_FRAME); net_event_init(&slirp->tx_event); net_event_init(&slirp->stop_event); @@ -485,6 +482,9 @@ net_slirp_close(void *priv) net_event_close(&slirp->tx_event); net_event_close(&slirp->stop_event); slirp_cleanup(slirp->slirp); + for (int i = 0; i < SLIRP_PKT_BATCH; i++) { + free(slirp->pkt_tx_v[i].data); + } free(slirp->pkt.data); free(slirp); slirp_card_num--; diff --git a/src/network/network.c b/src/network/network.c index a406140c4..a01eb938e 100644 --- a/src/network/network.c +++ b/src/network/network.c @@ -240,12 +240,11 @@ network_init(void) #endif } -static void +void network_queue_init(netqueue_t *queue) { - queue->size = NET_QUEUE_LEN; queue->head = queue->tail = 0; - for (int i=0; isize; i++) { + for (int i=0; ipackets[i].data = calloc(1, NET_MAX_FRAME); queue->packets[i].len = 0; } @@ -255,7 +254,7 @@ network_queue_init(netqueue_t *queue) static bool network_queue_full(netqueue_t *queue) { - return ((queue->head + 1) % queue->size) == queue->tail; + return ((queue->head + 1) & NET_QUEUE_LEN_MASK) == queue->tail; } static bool @@ -264,30 +263,50 @@ network_queue_empty(netqueue_t *queue) return (queue->head == queue->tail); } +static inline void +network_swap_packet(netpkt_t *pkt1, netpkt_t *pkt2) +{ + netpkt_t tmp = *pkt2; + *pkt2 = *pkt1; + *pkt1 = tmp; +} + int network_queue_put(netqueue_t *queue, uint8_t *data, int len) { - if (len > NET_MAX_FRAME || network_queue_full(queue)) { + if (len == 0 || len > NET_MAX_FRAME || network_queue_full(queue)) { return 0; } netpkt_t *pkt = &queue->packets[queue->head]; memcpy(pkt->data, data, len); pkt->len = len; - queue->head = (queue->head + 1) % queue->size; + queue->head = (queue->head + 1) & NET_QUEUE_LEN_MASK; + return 1; +} + +int +network_queue_put_swap(netqueue_t *queue, netpkt_t *src_pkt) +{ + if (src_pkt->len == 0 || src_pkt->len > NET_MAX_FRAME || network_queue_full(queue)) { + return 0; + } + + netpkt_t *dst_pkt = &queue->packets[queue->head]; + network_swap_packet(src_pkt, dst_pkt); + + queue->head = (queue->head + 1) & NET_QUEUE_LEN_MASK; return 1; } static int -network_queue_get(netqueue_t *queue, netpkt_t *dst_pkt) { +network_queue_get_swap(netqueue_t *queue, netpkt_t *dst_pkt) { if (network_queue_empty(queue)) return 0; - netpkt_t *pkt = &queue->packets[queue->tail]; - memcpy(dst_pkt->data, pkt->data, pkt->len); - dst_pkt->len = pkt->len; - queue->tail = (queue->tail + 1) % queue->size; - + netpkt_t *src_pkt = &queue->packets[queue->tail]; + network_swap_packet(src_pkt, dst_pkt); + queue->tail = (queue->tail + 1) & NET_QUEUE_LEN_MASK; return 1; } @@ -303,23 +322,18 @@ network_queue_move(netqueue_t *dst_q, netqueue_t *src_q) netpkt_t *src_pkt = &src_q->packets[src_q->tail]; netpkt_t *dst_pkt = &dst_q->packets[dst_q->head]; - uint8_t *tmp_dat = dst_pkt->data; - dst_pkt->data = src_pkt->data; - dst_pkt->len = src_pkt->len; - dst_q->head = (dst_q->head + 1) % dst_q->size; + network_swap_packet(src_pkt, dst_pkt); + dst_q->head = (dst_q->head + 1) & NET_QUEUE_LEN_MASK; + src_q->tail = (src_q->tail + 1) & NET_QUEUE_LEN_MASK; - src_pkt->data = tmp_dat; - src_pkt->len = 0; - src_q->tail = (src_q->tail + 1) % src_q->size; - - return 1; + return dst_pkt->len; } -static void +void network_queue_clear(netqueue_t *queue) { - for (int i=0; isize; i++) { + for (int i=0; ipackets[i].data); queue->packets[i].len = 0; } @@ -331,41 +345,47 @@ static void network_rx_queue(void *priv) { netcard_t *card = (netcard_t *)priv; - double timer_period; - int ret = 0; - bool activity = false; + uint32_t rx_bytes = 0; + for (int i = 0; i < NET_QUEUE_LEN; i++) { + if (card->queued_pkt.len == 0) { + thread_wait_mutex(card->rx_mutex); + int res = network_queue_get_swap(&card->queues[NET_QUEUE_RX], &card->queued_pkt); + thread_release_mutex(card->rx_mutex); + if (!res) + break; + } - if (card->queued_pkt.len == 0) { - thread_wait_mutex(card->rx_mutex); - network_queue_get(&card->queues[NET_QUEUE_RX], &card->queued_pkt); - thread_release_mutex(card->rx_mutex); - } - - if (card->queued_pkt.len > 0) { network_dump_packet(&card->queued_pkt); - ret = card->rx(card->card_drv, card->queued_pkt.data, card->queued_pkt.len); - } - - if (ret) { - activity = true; - timer_period = 0.762939453125 * ((card->queued_pkt.len >= 128) ? ((double) card->queued_pkt.len) : 128.0); + int res = card->rx(card->card_drv, card->queued_pkt.data, card->queued_pkt.len); + if (!res) + break; + rx_bytes += card->queued_pkt.len; card->queued_pkt.len = 0; - } else { - timer_period = 0.762939453125 * 128.0; } - timer_on_auto(&card->timer, timer_period); /* Transmission. */ + uint32_t tx_bytes = 0; thread_wait_mutex(card->tx_mutex); - ret = network_queue_move(&card->queues[NET_QUEUE_TX_HOST], &card->queues[NET_QUEUE_TX_VM]); + for (int i = 0; i < NET_QUEUE_LEN; i++) { + uint32_t bytes = network_queue_move(&card->queues[NET_QUEUE_TX_HOST], &card->queues[NET_QUEUE_TX_VM]); + if (!bytes) + break; + tx_bytes += bytes; + } thread_release_mutex(card->tx_mutex); - if (ret) { + if (tx_bytes) { /* Notify host that a packet is available in the TX queue */ card->host_drv.notify_in(card->host_drv.priv); - activity = true; } + double timer_period = 0.762939453125 * (rx_bytes > tx_bytes ? rx_bytes : tx_bytes); + if (timer_period < 200) + timer_period = 200; + + timer_on_auto(&card->timer, timer_period); + + bool activity = rx_bytes || tx_bytes; ui_sb_update_icon(SB_NETWORK, activity); } @@ -498,12 +518,29 @@ int network_tx_pop(netcard_t *card, netpkt_t *out_pkt) int ret = 0; thread_wait_mutex(card->tx_mutex); - ret = network_queue_get(&card->queues[NET_QUEUE_TX_HOST], out_pkt); + ret = network_queue_get_swap(&card->queues[NET_QUEUE_TX_HOST], out_pkt); thread_release_mutex(card->tx_mutex); return ret; } +int network_tx_popv(netcard_t *card, netpkt_t *pkt_vec, int vec_size) +{ + int pkt_count = 0; + + netqueue_t *queue = &card->queues[NET_QUEUE_TX_HOST]; + thread_wait_mutex(card->tx_mutex); + for (int i = 0; i < vec_size; i++) { + if (!network_queue_get_swap(queue, pkt_vec)) + break; + pkt_count++; + pkt_vec++; + } + thread_release_mutex(card->tx_mutex); + + return pkt_count; +} + int network_rx_put(netcard_t *card, uint8_t *bufp, int len) { int ret = 0; @@ -515,6 +552,17 @@ int network_rx_put(netcard_t *card, uint8_t *bufp, int len) return ret; } +int network_rx_put_pkt(netcard_t *card, netpkt_t *pkt) +{ + int ret = 0; + + thread_wait_mutex(card->rx_mutex); + ret = network_queue_put_swap(&card->queues[NET_QUEUE_RX], pkt); + thread_release_mutex(card->rx_mutex); + + return ret; +} + int network_dev_to_id(char *devname) {