netmap.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. /*
  2. * Copyright (c) 2013 Fred Klassen <fklassen at appneta dot com> - AppNeta
  3. *
  4. * The Tcpreplay Suite of tools is free software: you can redistribute it
  5. * and/or modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation, either version 3 of the
  7. * License, or with the authors permission any later version.
  8. *
  9. * The Tcpreplay Suite is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with the Tcpreplay Suite. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include "netmap.h"
  18. #include "config.h"
  19. #include "common.h"
  20. #include "tcpreplay_api.h"
  21. #include <errno.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <sys/ioctl.h>
  25. #include <sys/mman.h>
  26. #include <sys/socket.h>
  27. #include <sys/types.h>
  28. #include <unistd.h>
  29. static int nm_do_ioctl(sendpacket_t *sp, u_long what, int subcmd);
  30. /**
  31. * Method takes an open "/dev/netmap" file descriptor and returns
  32. * the netmap version.
  33. *
  34. * Returns -1 on error
  35. */
  36. int
  37. get_netmap_version()
  38. {
  39. u_int32_t netmap_version = -1;
  40. nmreq_t nmr;
  41. int fd;
  42. if ((fd = open("/dev/netmap", O_RDWR)) < 0)
  43. return -1;
  44. /* netmap version discovery */
  45. bzero(&nmr, sizeof(nmr));
  46. nmr.nr_version = NETMAP_API;
  47. /* attempt using the netmap API version that this was compiled under */
  48. if (ioctl(fd, NIOCGINFO, &nmr) == 0) {
  49. netmap_version = nmr.nr_version;
  50. dbgx(1, "netmap detected API version %d which matches compiled version\n", netmap_version);
  51. } else {
  52. /* failed.
  53. *
  54. * Try other versions in an attempt to find the version
  55. * that matches this system.
  56. */
  57. int x;
  58. for (x = 0; x < 50; ++x) {
  59. bzero(&nmr, sizeof(nmr));
  60. nmr.nr_version = x;
  61. if (ioctl(fd, NIOCGINFO, &nmr) == 0) {
  62. netmap_version = nmr.nr_version;
  63. dbgx(1,
  64. "netmap detected API version %d which doesn't match compiled version %d\n",
  65. netmap_version,
  66. NETMAP_API);
  67. break;
  68. }
  69. }
  70. }
  71. close(fd);
  72. return (int)netmap_version;
  73. }
  74. /**
  75. * ioctl support for netmap
  76. */
  77. static int
  78. nm_do_ioctl(sendpacket_t *sp, u_long what, int subcmd)
  79. {
  80. struct ifreq ifr;
  81. int error;
  82. int fd;
  83. #ifdef linux
  84. struct ethtool_value eval;
  85. #endif
  86. assert(sp);
  87. fd = socket(AF_INET, SOCK_DGRAM, 0);
  88. if (fd < 0) {
  89. dbg(1, "ioctl error: cannot get device control socket.\n");
  90. return -1;
  91. }
  92. bzero(&ifr, sizeof(ifr));
  93. strncpy(ifr.ifr_name, sp->device, sizeof(ifr.ifr_name));
  94. switch (what) {
  95. case SIOCSIFFLAGS:
  96. ifr.ifr_flags = sp->if_flags >> 16;
  97. ifr.ifr_flags = sp->if_flags & 0xffff;
  98. break;
  99. #ifdef linux
  100. case SIOCETHTOOL:
  101. eval.cmd = subcmd;
  102. eval.data = sp->data;
  103. ifr.ifr_data = (caddr_t)&eval;
  104. dbgx(1, "ioctl SIOCETHTOOL subcmd=%d data=%u", subcmd, eval.data);
  105. break;
  106. #endif
  107. }
  108. error = ioctl(fd, what, &ifr);
  109. if (error)
  110. goto done;
  111. switch (what) {
  112. case SIOCGIFFLAGS:
  113. sp->if_flags = (ifr.ifr_flags << 16) | (0xffff & ifr.ifr_flags);
  114. dbgx(1, "SIOCGIFFLAGS flags are 0x%x", sp->if_flags);
  115. break;
  116. #ifdef linux
  117. case SIOCETHTOOL:
  118. switch (subcmd) {
  119. case ETHTOOL_GGSO:
  120. sp->gso = eval.data;
  121. dbgx(1, "ioctl SIOCETHTOOL ETHTOOL_GGSO=%u", eval.data);
  122. break;
  123. case ETHTOOL_GTSO:
  124. sp->tso = eval.data;
  125. dbgx(1, "ioctl SIOCETHTOOL ETHTOOL_GTSO=%u", eval.data);
  126. break;
  127. case ETHTOOL_GRXCSUM:
  128. sp->rxcsum = eval.data;
  129. dbgx(1, "ioctl SIOCETHTOOL ETHTOOL_GRXCSUM=%u", eval.data);
  130. break;
  131. case ETHTOOL_GTXCSUM:
  132. sp->txcsum = eval.data;
  133. dbgx(1, "ioctl SIOCETHTOOL ETHTOOL_GTXCSUM=%u", eval.data);
  134. break;
  135. default:
  136. return -1;
  137. }
  138. break;
  139. #endif
  140. default:
  141. return -1;
  142. }
  143. done:
  144. close(fd);
  145. if (error)
  146. warnx("ioctl error %d %lu:%d", error, what, subcmd);
  147. return error;
  148. }
  149. /**
  150. * Inner sendpacket_open() method for using netmap
  151. */
  152. void *
  153. sendpacket_open_netmap(const char *device, char *errbuf, void *arg)
  154. {
  155. tcpreplay_t *ctx = (tcpreplay_t *)arg;
  156. sendpacket_t *sp = NULL;
  157. nmreq_t nmr;
  158. char ifname_buf[MAX_IFNAMELEN];
  159. const char *ifname;
  160. const char *port = NULL;
  161. size_t namelen;
  162. u_int32_t nr_ringid = 0;
  163. u_int32_t nr_flags = NR_REG_DEFAULT;
  164. int is_default = 0;
  165. assert(device);
  166. assert(errbuf);
  167. dbg(1, "sendpacket_open_netmap: using netmap");
  168. bzero(&nmr, sizeof(nmr));
  169. /* prep & return our sp handle */
  170. sp = (sendpacket_t *)safe_malloc(sizeof(sendpacket_t));
  171. if (strlen(device) > MAX_IFNAMELEN - 8) {
  172. snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "Interface name is to long: %s\n", device);
  173. goto IFACENAME_INVALID;
  174. }
  175. /* get the version of the netmap driver. If < 0, driver is not installed */
  176. sp->netmap_version = get_netmap_version();
  177. if (sp->netmap_version < 0) {
  178. snprintf(errbuf,
  179. SENDPACKET_ERRBUF_SIZE,
  180. "Unable to determine the running netmap version.\n"
  181. "See INSTALL document for details on installing or upgrading netmap.");
  182. goto NETMAP_NOT_INSTALLED;
  183. }
  184. /*
  185. * Sort out interface names
  186. *
  187. * ifname (foo, netmap:foo or vale:foo) is the port name
  188. * foo bind to a single NIC hardware queue
  189. * netmap:foo bind to a single NIC hardware queue
  190. * vale:foo bind to the Vale virtual interface
  191. *
  192. * for netmap version 10+ a suffix can indicate the following:
  193. * netmap:foo! bind to all NIC hardware queues (may cause TX reordering)
  194. * netmap:foo^ bind to the host (sw) ring pair
  195. * netmap:foo* bind to the host (sw) and NIC ring pairs (transparent)
  196. * netmap:foo-NN bind to the individual NIC ring pair (queue) where NN = the ring number
  197. * netmap:foo{NN bind to the master side of pipe NN
  198. * netmap:foo}NN bind to the slave side of pipe NN
  199. */
  200. if (strncmp(device, "netmap:", 7) && strncmp(device, "vale", 4)) {
  201. snprintf(ifname_buf, sizeof(ifname_buf), "netmap:%s", device);
  202. ifname = ifname_buf;
  203. } else {
  204. ifname = device;
  205. }
  206. if (!strncmp("vale", device, 4))
  207. sp->is_vale = 1;
  208. if (ifname[0] == 'n')
  209. ifname += 7;
  210. /* scan for a separator */
  211. for (port = ifname; *port && !index("!-*^{}", *port); port++)
  212. ;
  213. namelen = port - ifname;
  214. if (namelen > sizeof(nmr.nr_name)) {
  215. snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "Interface name is to long: %s\n", device);
  216. goto IFACENAME_INVALID;
  217. }
  218. /*
  219. * Open the netmap device to fetch the number of queues of our
  220. * interface.
  221. *
  222. * The first NIOCREGIF also detaches the card from the
  223. * protocol stack and may cause a reset of the card,
  224. * which in turn may take some time for the PHY to
  225. * reconfigure.
  226. */
  227. if ((sp->handle.fd = open("/dev/netmap", O_RDWR)) < 0) {
  228. dbg(1, "sendpacket_open_netmap: Unable to access netmap");
  229. snprintf(errbuf,
  230. SENDPACKET_ERRBUF_SIZE,
  231. "Unable to access netmap.\n"
  232. "See INSTALL to learn which NICs are supported and\n"
  233. "how to set up netmap-capable network drivers.");
  234. goto OPEN_FAILED;
  235. }
  236. /*
  237. * The nmreq structure must have the NETMAP_API version for the running machine.
  238. * However the binary may have been compiled on a different machine than the
  239. * running machine. Discover the true netmap API version, and be careful to call
  240. * functions that are available on all netmap versions.
  241. */
  242. if (sp->netmap_version >= 10) {
  243. switch (*port) {
  244. case '-': /* one NIC */
  245. nr_flags = NR_REG_ONE_NIC;
  246. nr_ringid = strtol(port + 1, NULL, 10);
  247. break;
  248. case '*': /* NIC and SW, ignore port */
  249. nr_flags = NR_REG_NIC_SW;
  250. if (port[1]) {
  251. snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "invalid netmap port for nic+sw");
  252. goto NETMAP_IF_PARSE_FAIL;
  253. }
  254. break;
  255. case '^': /* only sw ring */
  256. nr_flags = NR_REG_SW;
  257. if (port[1]) {
  258. snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "invalid port for sw ring");
  259. goto NETMAP_IF_PARSE_FAIL;
  260. }
  261. break;
  262. case '{':
  263. nr_flags = NR_REG_PIPE_MASTER;
  264. nr_ringid = strtol(port + 1, NULL, 10);
  265. break;
  266. case '}':
  267. nr_flags = NR_REG_PIPE_SLAVE;
  268. nr_ringid = strtol(port + 1, NULL, 10);
  269. break;
  270. case '!':
  271. nr_flags = NR_REG_ALL_NIC;
  272. break;
  273. default: /* '\0', no suffix */
  274. nr_flags = NR_REG_ALL_NIC;
  275. is_default = 1;
  276. break;
  277. }
  278. if (nr_ringid >= NETMAP_RING_MASK) {
  279. snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "invalid ringid");
  280. goto NETMAP_IF_PARSE_FAIL;
  281. }
  282. nmr.nr_ringid = nr_ringid;
  283. nmr.nr_flags = nr_flags;
  284. }
  285. nmr.nr_version = sp->netmap_version;
  286. memcpy(nmr.nr_name, ifname, namelen);
  287. nmr.nr_name[namelen] = '\0';
  288. strlcpy(sp->device, nmr.nr_name, sizeof(sp->device));
  289. /*
  290. * Register the interface on the netmap device: from now on,
  291. * we can operate on the network interface without any
  292. * interference from the legacy network stack.
  293. *
  294. * Cards take a long time to reset the PHY.
  295. */
  296. fprintf(stderr, "Switching network driver for %s to netmap bypass mode... ", sp->device);
  297. fflush(NULL);
  298. sleep(1); /* ensure message prints when user is connected via ssh */
  299. if (ioctl(sp->handle.fd, NIOCREGIF, &nmr)) {
  300. snprintf(errbuf,
  301. SENDPACKET_ERRBUF_SIZE,
  302. "Failure accessing netmap.\n"
  303. "\tRequest for netmap version %d failed.\n\tCompiled netmap driver is version %d.\n\tError=%s\n",
  304. sp->netmap_version,
  305. NETMAP_API,
  306. strerror(errno));
  307. goto NETMAP_IF_FAILED;
  308. }
  309. if (!nmr.nr_memsize) {
  310. snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "Netmap interface '%s' not configured.\n", device);
  311. goto NETMAP_IF_FAILED;
  312. }
  313. sp->mmap_size = nmr.nr_memsize;
  314. sp->mmap_addr = (struct netmap_d *)mmap(0, sp->mmap_size, PROT_WRITE | PROT_READ, MAP_SHARED, sp->handle.fd, 0);
  315. if (!sp->mmap_addr || sp->mmap_addr == MAP_FAILED) {
  316. snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "mmap: %s", strerror(errno));
  317. goto MMAP_FAILED;
  318. }
  319. dbgx(1, "sendpacket_open_netmap: mapping %d Kbytes queues=%d", sp->mmap_size >> 10, nmr.nr_tx_rings);
  320. sp->nm_if = NETMAP_IF(sp->mmap_addr, nmr.nr_offset);
  321. sp->nmr = nmr;
  322. sp->handle_type = SP_TYPE_NETMAP;
  323. /* set up ring IDs */
  324. sp->cur_tx_ring = 0;
  325. switch (nr_flags) {
  326. case NR_REG_DEFAULT: /* only use one queue to prevent TX reordering */
  327. sp->first_tx_ring = sp->last_tx_ring = sp->cur_tx_ring = 0;
  328. break;
  329. case NR_REG_ALL_NIC:
  330. if (is_default) {
  331. sp->first_tx_ring = sp->last_tx_ring = sp->cur_tx_ring = 0;
  332. } else {
  333. sp->first_tx_ring = sp->cur_tx_ring = 0;
  334. sp->last_tx_ring = nmr.nr_tx_rings - 1;
  335. }
  336. break;
  337. case NR_REG_SW:
  338. sp->first_tx_ring = sp->last_tx_ring = sp->cur_tx_ring = nmr.nr_tx_rings;
  339. break;
  340. case NR_REG_NIC_SW:
  341. sp->first_tx_ring = sp->cur_tx_ring = 0;
  342. sp->last_tx_ring = nmr.nr_tx_rings;
  343. break;
  344. case NR_REG_ONE_NIC:
  345. sp->first_tx_ring = sp->last_tx_ring = sp->cur_tx_ring = nr_ringid;
  346. break;
  347. default:
  348. sp->first_tx_ring = sp->last_tx_ring = sp->cur_tx_ring = 0;
  349. }
  350. {
  351. /* debugging code */
  352. int i;
  353. dbgx(1, "%s tx first=%d last=%d num=%d", ifname, sp->first_tx_ring, sp->last_tx_ring, sp->nmr.nr_tx_rings);
  354. for (i = 0; i <= sp->nmr.nr_tx_rings; i++) {
  355. #ifdef HAVE_NETMAP_RING_HEAD_TAIL
  356. dbgx(1,
  357. "TX%d 0x%p head=%d cur=%d tail=%d",
  358. i,
  359. NETMAP_TXRING(sp->nm_if, i),
  360. (NETMAP_TXRING(sp->nm_if, i))->head,
  361. (NETMAP_TXRING(sp->nm_if, i))->cur,
  362. (NETMAP_TXRING(sp->nm_if, i))->tail);
  363. #else
  364. dbgx(1,
  365. "TX%d 0x%p cur=%d avail=%d",
  366. i,
  367. NETMAP_TXRING(sp->nm_if, i),
  368. (NETMAP_TXRING(sp->nm_if, i))->cur,
  369. (NETMAP_TXRING(sp->nm_if, i))->avail);
  370. #endif
  371. }
  372. }
  373. dbgx(2, "Waiting %d seconds for phy reset...", ctx->options->netmap_delay);
  374. sleep(ctx->options->netmap_delay);
  375. dbg(2, "Ready!");
  376. if (!sp->is_vale) {
  377. if (nm_do_ioctl(sp, SIOCGIFFLAGS, 0) < 0)
  378. goto NM_DO_IOCTL_FAILED;
  379. if ((sp->if_flags & IFF_RUNNING) == 0) {
  380. dbgx(1, "sendpacket_open_netmap: %s is not running", sp->device);
  381. snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "interface %s is not running - check cables\n", sp->device);
  382. goto NETMAP_IF_NOT_RUNNING;
  383. }
  384. if ((sp->if_flags & IFF_UP) == 0) {
  385. dbgx(1, "%s is down, bringing up...", sp->device);
  386. sp->if_flags |= IFF_UP;
  387. }
  388. /* set promiscuous mode */
  389. sp->if_flags |= IFF_PROMISC;
  390. if (nm_do_ioctl(sp, SIOCSIFFLAGS, 0) < 0)
  391. goto NM_DO_IOCTL_FAILED;
  392. #ifdef linux
  393. /* disable:
  394. * - generic-segmentation-offload
  395. * - tcp-segmentation-offload
  396. * - rx-checksumming
  397. * - tx-checksumming
  398. */
  399. if (nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_GGSO) < 0 || nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_GTSO) < 0 ||
  400. nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_GRXCSUM) < 0 || nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_GTXCSUM) < 0)
  401. goto NM_DO_IOCTL_FAILED;
  402. sp->data = 0;
  403. if (nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_SGSO) < 0 || nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_STSO) < 0 ||
  404. nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_SRXCSUM) < 0 || nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_STXCSUM))
  405. goto NM_DO_IOCTL_FAILED;
  406. #endif
  407. }
  408. if (sp->abort)
  409. goto NETMAP_ABORT;
  410. notice("done!");
  411. return sp;
  412. NM_DO_IOCTL_FAILED:
  413. snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "nm_do_ioctl: %s", strerror(errno));
  414. NETMAP_IF_NOT_RUNNING:
  415. notice("failed!");
  416. NETMAP_ABORT:
  417. fprintf(stderr, " Switching network driver for %s to normal mode... ", sp->device);
  418. fflush(NULL);
  419. munmap(sp->mmap_addr, sp->mmap_size);
  420. MMAP_FAILED:
  421. #if NETMAP_API < 10
  422. ioctl(sp->handle.fd, NIOCUNREGIF, NULL);
  423. #endif
  424. NETMAP_IF_FAILED:
  425. NETMAP_IF_PARSE_FAIL:
  426. close(sp->handle.fd);
  427. OPEN_FAILED:
  428. safe_free(sp);
  429. IFACENAME_INVALID:
  430. NETMAP_NOT_INSTALLED:
  431. return NULL;
  432. }
  433. void
  434. sendpacket_close_netmap(void *p)
  435. {
  436. sendpacket_t *sp = p;
  437. fprintf(stderr, "Switching network driver for %s to normal mode... ", sp->device);
  438. fflush(NULL);
  439. /* flush any remaining packets */
  440. ioctl(sp->handle.fd, NIOCTXSYNC, NULL);
  441. /* wait for traffic to be sent */
  442. dbgx(2, "Waiting %d seconds for phy reset...", sp->netmap_delay);
  443. sleep(sp->netmap_delay);
  444. dbg(2, "Ready!");
  445. #ifdef linux
  446. if (!sp->is_vale) {
  447. /* restore original settings:
  448. * - generic-segmentation-offload
  449. * - tcp-segmentation-offload
  450. * - rx-checksumming
  451. * - tx-checksumming
  452. */
  453. sp->data = sp->gso;
  454. nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_SGSO);
  455. sp->data = sp->tso;
  456. nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_STSO);
  457. sp->data = sp->rxcsum;
  458. nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_SRXCSUM);
  459. sp->data = sp->txcsum;
  460. nm_do_ioctl(sp, SIOCETHTOOL, ETHTOOL_STXCSUM);
  461. }
  462. #endif /* linux */
  463. /* restore interface to normal mode */
  464. #if NETMAP_API < 10
  465. ioctl(sp->handle.fd, NIOCUNREGIF, NULL);
  466. #endif
  467. if (sp->mmap_addr)
  468. munmap(sp->mmap_addr, sp->mmap_size);
  469. close(sp->handle.fd);
  470. notice("done!");
  471. }
  472. bool
  473. netmap_tx_queues_empty(void *p)
  474. {
  475. sendpacket_t *sp = p;
  476. struct netmap_ring *txring;
  477. assert(sp);
  478. sp->cur_tx_ring = 0;
  479. txring = NETMAP_TXRING(sp->nm_if, sp->cur_tx_ring);
  480. while (NETMAP_TX_RING_EMPTY(txring)) {
  481. /* current ring is empty- go to next */
  482. ++sp->cur_tx_ring;
  483. if (sp->cur_tx_ring > sp->last_tx_ring) {
  484. /* last ring */
  485. sp->cur_tx_ring = 0;
  486. return true;
  487. }
  488. txring = NETMAP_TXRING(sp->nm_if, sp->cur_tx_ring);
  489. }
  490. /*
  491. * send TX interrupt signal
  492. */
  493. ioctl(sp->handle.fd, NIOCTXSYNC, NULL);
  494. return false;
  495. }
  496. int
  497. sendpacket_send_netmap(void *p, const u_char *data, size_t len)
  498. {
  499. sendpacket_t *sp = p;
  500. struct netmap_ring *txring;
  501. struct netmap_slot *slot;
  502. char *pkt;
  503. uint32_t cur, avail;
  504. if (sp->abort)
  505. return 0;
  506. txring = NETMAP_TXRING(sp->nm_if, sp->cur_tx_ring);
  507. while ((avail = nm_ring_space(txring)) == 0) {
  508. /* out of space on current TX queue - go to next */
  509. ++sp->cur_tx_ring;
  510. if (sp->cur_tx_ring > sp->last_tx_ring) {
  511. /*
  512. * out of space on all queues
  513. *
  514. * we have looped through all configured TX queues
  515. * so we have to reset to the first queue and
  516. * wait for available space
  517. */
  518. sp->cur_tx_ring = sp->first_tx_ring;
  519. /* send TX interrupt signal
  520. *
  521. * On Linux this makes one slot free on the
  522. * ring, which increases speed by about 10Mbps.
  523. *
  524. * But it will never free up all the slots. For
  525. * that we must poll and call again.
  526. */
  527. ioctl(sp->handle.fd, NIOCTXSYNC, NULL);
  528. /* loop again */
  529. return -2;
  530. }
  531. txring = NETMAP_TXRING(sp->nm_if, sp->cur_tx_ring);
  532. }
  533. /*
  534. * send
  535. */
  536. cur = txring->cur;
  537. slot = &txring->slot[cur];
  538. slot->flags = 0;
  539. pkt = NETMAP_BUF(txring, slot->buf_idx);
  540. memcpy(pkt, data, min(len, txring->nr_buf_size));
  541. slot->len = len;
  542. if (avail <= 1)
  543. slot->flags = NS_REPORT;
  544. dbgx(3,
  545. "netmap cur=%d slot index=%d flags=0x%x empty=%d avail=%u bufsize=%d\n",
  546. cur,
  547. slot->buf_idx,
  548. slot->flags,
  549. NETMAP_TX_RING_EMPTY(txring),
  550. nm_ring_space(txring),
  551. txring->nr_buf_size);
  552. /* let kernel know that packet is available */
  553. cur = NETMAP_RING_NEXT(txring, cur);
  554. #ifdef HAVE_NETMAP_RING_HEAD_TAIL
  555. txring->head = cur;
  556. #else
  557. txring->avail--;
  558. #endif
  559. txring->cur = cur;
  560. return len;
  561. }