aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--example/generator/odp_generator.c3
-rw-r--r--example/ipsec/odp_ipsec.c3
-rw-r--r--example/packet/odp_pktio.c3
-rw-r--r--platform/linux-generic/Makefile.am2
-rw-r--r--platform/linux-generic/include/odp_packet_io_internal.h3
-rw-r--r--platform/linux-generic/include/odp_packet_netmap.h24
-rw-r--r--platform/linux-generic/m4/configure.m41
-rw-r--r--platform/linux-generic/m4/odp_netmap.m450
-rw-r--r--platform/linux-generic/pktio/io_ops.c3
-rw-r--r--platform/linux-generic/pktio/netmap.c303
-rw-r--r--platform/linux-generic/pktio/socket.c4
-rw-r--r--test/performance/odp_l2fwd.c3
13 files changed, 400 insertions, 4 deletions
diff --git a/configure.ac b/configure.ac
index 15dc6cf..92f9d39 100644
--- a/configure.ac
+++ b/configure.ac
@@ -270,6 +270,8 @@ ODP_CFLAGS="$ODP_CFLAGS -Wmissing-declarations -Wold-style-definition -Wpointer-
ODP_CFLAGS="$ODP_CFLAGS -Wcast-align -Wnested-externs -Wcast-qual -Wformat-nonliteral"
ODP_CFLAGS="$ODP_CFLAGS -Wformat-security -Wundef -Wwrite-strings"
ODP_CFLAGS="$ODP_CFLAGS -std=c99"
+# Extra flags for example to suppress certain warning types
+ODP_CFLAGS="$ODP_CFLAGS $ODP_CFLAGS_EXTRA"
##########################################################################
# Default include setup
diff --git a/example/generator/odp_generator.c b/example/generator/odp_generator.c
index f4f6038..085902b 100644
--- a/example/generator/odp_generator.c
+++ b/example/generator/odp_generator.c
@@ -1114,7 +1114,8 @@ static void usage(char *progname)
"\n"
"Optional OPTIONS\n"
" -h, --help Display help and exit.\n"
- " environment variables: ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
+ " environment variables: ODP_PKTIO_DISABLE_NETMAP\n"
+ " ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
" ODP_PKTIO_DISABLE_SOCKET_MMSG\n"
" can be used to advanced pkt I/O selection for linux-generic\n"
" -p, --packetsize payload length of the packets\n"
diff --git a/example/ipsec/odp_ipsec.c b/example/ipsec/odp_ipsec.c
index 564d65e..6f5aae8 100644
--- a/example/ipsec/odp_ipsec.c
+++ b/example/ipsec/odp_ipsec.c
@@ -1577,7 +1577,8 @@ static void usage(char *progname)
"Optional OPTIONS\n"
" -c, --count <number> CPU count.\n"
" -h, --help Display help and exit.\n"
- " environment variables: ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
+ " environment variables: ODP_PKTIO_DISABLE_NETMAP\n"
+ " ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
" ODP_PKTIO_DISABLE_SOCKET_MMSG\n"
" can be used to advanced pkt I/O selection for linux-generic\n"
" ODP_IPSEC_USE_POLL_QUEUES\n"
diff --git a/example/packet/odp_pktio.c b/example/packet/odp_pktio.c
index 16d4533..de6e42b 100644
--- a/example/packet/odp_pktio.c
+++ b/example/packet/odp_pktio.c
@@ -700,7 +700,8 @@ static void usage(char *progname)
" 1: Receive and send via queues.\n"
" 2: Receive via scheduler, send via queues.\n"
" -h, --help Display help and exit.\n"
- " environment variables: ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
+ " environment variables: ODP_PKTIO_DISABLE_NETMAP\n"
+ " ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
" ODP_PKTIO_DISABLE_SOCKET_MMSG\n"
" can be used to advanced pkt I/O selection for linux-generic\n"
"\n", NO_PATH(progname), NO_PATH(progname)
diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index 4c79730..2a71d32 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -127,6 +127,7 @@ noinst_HEADERS = \
${srcdir}/include/odp_packet_internal.h \
${srcdir}/include/odp_packet_io_internal.h \
${srcdir}/include/odp_packet_io_queue.h \
+ ${srcdir}/include/odp_packet_netmap.h \
${srcdir}/include/odp_packet_socket.h \
${srcdir}/include/odp_pool_internal.h \
${srcdir}/include/odp_queue_internal.h \
@@ -151,6 +152,7 @@ __LIB__libodp_la_SOURCES = \
odp_packet_io.c \
pktio/io_ops.c \
pktio/loop.c \
+ pktio/netmap.c \
pktio/socket.c \
pktio/socket_mmap.c \
odp_pool.c \
diff --git a/platform/linux-generic/include/odp_packet_io_internal.h b/platform/linux-generic/include/odp_packet_io_internal.h
index 60aca1c..353b40d 100644
--- a/platform/linux-generic/include/odp_packet_io_internal.h
+++ b/platform/linux-generic/include/odp_packet_io_internal.h
@@ -21,6 +21,7 @@ extern "C" {
#include <odp/spinlock.h>
#include <odp/ticketlock.h>
#include <odp_packet_socket.h>
+#include <odp_packet_netmap.h>
#include <odp_classification_datamodel.h>
#include <odp_align_internal.h>
#include <odp_debug_internal.h>
@@ -50,6 +51,7 @@ struct pktio_entry {
pkt_sock_t pkt_sock; /**< using socket API for IO */
pkt_sock_mmap_t pkt_sock_mmap; /**< using socket mmap
* API for IO */
+ pkt_netmap_t pkt_nm; /**< using netmap API for IO */
};
enum {
STATE_START = 0,
@@ -122,6 +124,7 @@ static inline void pktio_cls_enabled_set(pktio_entry_t *entry, int ena)
int pktin_poll(pktio_entry_t *entry);
+extern const pktio_if_ops_t netmap_pktio_ops;
extern const pktio_if_ops_t sock_mmsg_pktio_ops;
extern const pktio_if_ops_t sock_mmap_pktio_ops;
extern const pktio_if_ops_t loopback_pktio_ops;
diff --git a/platform/linux-generic/include/odp_packet_netmap.h b/platform/linux-generic/include/odp_packet_netmap.h
new file mode 100644
index 0000000..23aea5b
--- /dev/null
+++ b/platform/linux-generic/include/odp_packet_netmap.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef ODP_PACKET_NETMAP_H
+#define ODP_PACKET_NETMAP_H
+
+#include <odp/pool.h>
+
+#include <linux/if_ether.h>
+
+/** Packet socket using netmap mmaped rings for both Rx and Tx */
+typedef struct {
+ odp_pool_t pool; /**< pool to alloc packets from */
+ size_t max_frame_len; /**< buf_size - sizeof(pkt_hdr) */
+ struct nm_desc *desc; /**< netmap meta-data for the device */
+ uint32_t if_flags; /**< interface flags */
+ int sockfd; /**< control socket */
+ unsigned char if_mac[ETH_ALEN]; /**< eth mac address */
+} pkt_netmap_t;
+
+#endif
diff --git a/platform/linux-generic/m4/configure.m4 b/platform/linux-generic/m4/configure.m4
index 9658274..f2d42f1 100644
--- a/platform/linux-generic/m4/configure.m4
+++ b/platform/linux-generic/m4/configure.m4
@@ -18,6 +18,7 @@ AC_LINK_IFELSE(
m4_include([platform/linux-generic/m4/odp_pthread.m4])
m4_include([platform/linux-generic/m4/odp_openssl.m4])
+m4_include([platform/linux-generic/m4/odp_netmap.m4])
AC_CONFIG_FILES([platform/linux-generic/Makefile
platform/linux-generic/test/Makefile
diff --git a/platform/linux-generic/m4/odp_netmap.m4 b/platform/linux-generic/m4/odp_netmap.m4
new file mode 100644
index 0000000..02cd3d3
--- /dev/null
+++ b/platform/linux-generic/m4/odp_netmap.m4
@@ -0,0 +1,50 @@
+##########################################################################
+# Enable netmap support
+##########################################################################
+netmap_support=no
+AC_ARG_ENABLE([netmap_support],
+ [ --enable-netmap-support include netmap IO support],
+ [if test x$enableval = xyes; then
+ netmap_support=yes
+ fi])
+
+##########################################################################
+# Set optional netmap path
+##########################################################################
+AC_ARG_WITH([netmap-path],
+AC_HELP_STRING([--with-netmap-path=DIR path to netmap root directory],
+ [(or in the default path if not specified).]),
+ [NETMAP_PATH=$withval
+ AM_CPPFLAGS="$AM_CPPFLAGS -I$NETMAP_PATH/sys"
+ netmap_support=yes],[])
+
+##########################################################################
+# Save and set temporary compilation flags
+##########################################################################
+OLD_CPPFLAGS=$CPPFLAGS
+CPPFLAGS="$AM_CPPFLAGS $CPPFLAGS"
+
+##########################################################################
+# Check for netmap availability
+##########################################################################
+if test x$netmap_support = xyes
+then
+ AC_CHECK_HEADERS([net/netmap_user.h], [],
+ [AC_MSG_FAILURE(["can't find netmap header"])])
+ ODP_CFLAGS="$ODP_CFLAGS -DODP_NETMAP"
+else
+ netmap_support=no
+fi
+
+AM_CONDITIONAL([netmap_support], [test x$netmap_support = xyes ])
+
+# Disable cast errors until the problem in netmap_user.h is fixed upstream
+if test x$netmap_support = xyes
+then
+ODP_CFLAGS_EXTRA="$ODP_CFLAGS_EXTRA -Wno-cast-qual"
+fi
+
+##########################################################################
+# Restore old saved variables
+##########################################################################
+CPPFLAGS=$OLD_CPPFLAGS \ No newline at end of file
diff --git a/platform/linux-generic/pktio/io_ops.c b/platform/linux-generic/pktio/io_ops.c
index 1d47e74..bd4cc48 100644
--- a/platform/linux-generic/pktio/io_ops.c
+++ b/platform/linux-generic/pktio/io_ops.c
@@ -12,6 +12,9 @@
* Array must be NULL terminated */
const pktio_if_ops_t * const pktio_if_ops[] = {
&loopback_pktio_ops,
+#ifdef ODP_NETMAP
+ &netmap_pktio_ops,
+#endif
&sock_mmap_pktio_ops,
&sock_mmsg_pktio_ops,
NULL
diff --git a/platform/linux-generic/pktio/netmap.c b/platform/linux-generic/pktio/netmap.c
new file mode 100644
index 0000000..54e7043
--- /dev/null
+++ b/platform/linux-generic/pktio/netmap.c
@@ -0,0 +1,303 @@
+/* Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifdef ODP_NETMAP
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <odp_packet_netmap.h>
+#include <odp_packet_socket.h>
+#include <odp_packet_io_internal.h>
+#include <odp_debug_internal.h>
+#include <odp/helper/eth.h>
+
+#include <sys/ioctl.h>
+#include <poll.h>
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+
+#define NETMAP_WITH_LIBS
+#include <net/netmap_user.h>
+
+static struct nm_desc mmap_desc; /** Used to store the mmap address;
+ filled in first time, used for
+ subsequent calls to nm_open */
+
+#define NM_INJECT_RETRIES 10
+
+struct dispatch_args {
+ odp_packet_t *pkt_table;
+ unsigned nb_rx;
+ pktio_entry_t *pktio_entry;
+};
+
+static int netmap_do_ioctl(pktio_entry_t *pktio_entry, unsigned long cmd,
+ int subcmd)
+{
+ pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm;
+ struct ethtool_value eval;
+ struct ifreq ifr;
+ int err;
+ int fd = pkt_nm->sockfd;
+
+ memset(&ifr, 0, sizeof(ifr));
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s",
+ pktio_entry->s.name);
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ ifr.ifr_flags = pkt_nm->if_flags & 0xffff;
+ break;
+ case SIOCETHTOOL:
+ eval.cmd = subcmd;
+ eval.data = 0;
+ ifr.ifr_data = (caddr_t)&eval;
+ break;
+ default:
+ break;
+ }
+ err = ioctl(fd, cmd, &ifr);
+ if (err)
+ goto done;
+
+ switch (cmd) {
+ case SIOCGIFFLAGS:
+ pkt_nm->if_flags = (ifr.ifr_flags << 16) |
+ (0xffff & ifr.ifr_flags);
+ break;
+ default:
+ break;
+ }
+done:
+ if (err)
+ ODP_ERR("ioctl err %d %lu: %s\n", err, cmd, strerror(errno));
+
+ return err;
+}
+
+static int netmap_close(pktio_entry_t *pktio_entry)
+{
+ pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm;
+
+ if (pkt_nm->desc != NULL)
+ nm_close(pkt_nm->desc);
+
+ if (pkt_nm->sockfd != -1 && close(pkt_nm->sockfd) != 0) {
+ __odp_errno = errno;
+ ODP_ERR("close(sockfd): %s\n", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int netmap_open(odp_pktio_t id ODP_UNUSED, pktio_entry_t *pktio_entry,
+ const char *netdev, odp_pool_t pool)
+{
+ char ifname[IFNAMSIZ + 7]; /* netmap:<ifname> */
+ int err;
+ int sockfd;
+ pkt_netmap_t *pkt_nm = &pktio_entry->s.pkt_nm;
+
+ if (getenv("ODP_PKTIO_DISABLE_NETMAP"))
+ return -1;
+
+ if (pool == ODP_POOL_INVALID)
+ return -1;
+
+ /* Init pktio entry */
+ memset(pkt_nm, 0, sizeof(*pkt_nm));
+ pkt_nm->sockfd = -1;
+ pkt_nm->pool = pool;
+
+ /* max frame len taking into account the l2-offset */
+ pkt_nm->max_frame_len = ODP_CONFIG_PACKET_BUF_LEN_MAX -
+ odp_buffer_pool_headroom(pool) -
+ odp_buffer_pool_tailroom(pool);
+
+ snprintf(pktio_entry->s.name, sizeof(pktio_entry->s.name), "%s",
+ netdev);
+ snprintf(ifname, sizeof(ifname), "netmap:%s", netdev);
+
+ if (mmap_desc.mem == NULL)
+ pkt_nm->desc = nm_open(ifname, NULL, NETMAP_NO_TX_POLL, NULL);
+ else
+ pkt_nm->desc = nm_open(ifname, NULL, NETMAP_NO_TX_POLL |
+ NM_OPEN_NO_MMAP, &mmap_desc);
+ if (pkt_nm->desc == NULL) {
+ ODP_ERR("nm_open(%s) failed\n", ifname);
+ goto error;
+ }
+
+ if (mmap_desc.mem == NULL) {
+ mmap_desc.mem = pkt_nm->desc->mem;
+ mmap_desc.memsize = pkt_nm->desc->memsize;
+ }
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd == -1) {
+ ODP_ERR("Cannot get device control socket\n");
+ goto error;
+ }
+ pkt_nm->sockfd = sockfd;
+
+ err = netmap_do_ioctl(pktio_entry, SIOCGIFFLAGS, 0);
+ if (err)
+ goto error;
+ if ((pkt_nm->if_flags & IFF_UP) == 0)
+ ODP_DBG("%s is down\n", pktio_entry->s.name);
+
+ err = netmap_do_ioctl(pktio_entry, SIOCETHTOOL, ETHTOOL_SGSO);
+ if (err)
+ ODP_DBG("ETHTOOL_SGSO not supported\n");
+
+ err = netmap_do_ioctl(pktio_entry, SIOCETHTOOL, ETHTOOL_STSO);
+ if (err)
+ ODP_DBG("ETHTOOL_STSO not supported\n");
+
+ err = mac_addr_get_fd(sockfd, netdev, pkt_nm->if_mac);
+ if (err)
+ goto error;
+
+ err = netmap_do_ioctl(pktio_entry, SIOCETHTOOL, ETHTOOL_STXCSUM);
+ if (err)
+ ODP_DBG("ETHTOOL_STXCSUM not supported\n");
+
+ return 0;
+
+error:
+ netmap_close(pktio_entry);
+ return -1;
+}
+
+static void netmap_recv_cb(u_char *arg, const struct nm_pkthdr *hdr,
+ const u_char *buf)
+{
+ struct dispatch_args *args = (struct dispatch_args *)arg;
+ pkt_netmap_t *pkt_nm = &args->pktio_entry->s.pkt_nm;
+ odp_packet_t pkt;
+ size_t frame_len = (size_t)hdr->len;
+
+ if (odp_unlikely(frame_len > pkt_nm->max_frame_len)) {
+ ODP_ERR("RX: frame too big %u %lu!\n", (unsigned)frame_len,
+ pkt_nm->max_frame_len);
+ return;
+ }
+
+ if (odp_unlikely(frame_len < ODPH_ETH_LEN_MIN)) {
+ ODP_ERR("RX: Frame truncated: %u\n", (unsigned)frame_len);
+ return;
+ }
+
+ pkt = odp_packet_alloc(pkt_nm->pool, frame_len);
+ if (pkt == ODP_PACKET_INVALID)
+ return;
+
+ /* For now copy the data in the mbuf,
+ worry about zero-copy later */
+ if (odp_packet_copydata_in(pkt, 0, frame_len, buf) != 0) {
+ odp_packet_free(pkt);
+ return;
+ }
+
+ _odp_packet_reset_parse(pkt);
+
+ args->pkt_table[args->nb_rx++] = pkt;
+}
+
+static int netmap_recv(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[],
+ unsigned num)
+{
+ struct dispatch_args args;
+ struct nm_desc *nm_desc = pktio_entry->s.pkt_nm.desc;
+ struct pollfd polld;
+
+ polld.fd = nm_desc->fd;
+ polld.events = POLLIN;
+
+ args.pkt_table = pkt_table;
+ args.nb_rx = 0;
+ args.pktio_entry = pktio_entry;
+
+ nm_dispatch(nm_desc, num, netmap_recv_cb, (u_char *)&args);
+ if (args.nb_rx == 0) {
+ if (odp_unlikely(poll(&polld, 1, 0) < 0))
+ ODP_ERR("RX: poll error\n");
+ }
+ return args.nb_rx;
+}
+
+static int netmap_send(pktio_entry_t *pktio_entry, odp_packet_t pkt_table[],
+ unsigned num)
+{
+ struct nm_desc *nm_desc = pktio_entry->s.pkt_nm.desc;
+ struct pollfd polld;
+ unsigned i, nb_tx;
+ uint8_t *frame;
+ uint32_t frame_len;
+
+ polld.fd = nm_desc->fd;
+ polld.events = POLLOUT;
+
+ for (nb_tx = 0; nb_tx < num; nb_tx++) {
+ frame_len = 0;
+ frame = odp_packet_l2_ptr(pkt_table[nb_tx], &frame_len);
+ for (i = 0; i < NM_INJECT_RETRIES; i++) {
+ if (nm_inject(nm_desc, frame, frame_len) == 0)
+ poll(&polld, 1, 0);
+ else
+ break;
+ }
+ if (odp_unlikely(i == NM_INJECT_RETRIES))
+ break;
+ }
+ for (i = 0; i < nb_tx; i++)
+ odp_packet_free(pkt_table[i]);
+
+ return nb_tx;
+}
+
+static int netmap_mac_addr_get(pktio_entry_t *pktio_entry, void *mac_addr)
+{
+ memcpy(mac_addr, pktio_entry->s.pkt_nm.if_mac, ETH_ALEN);
+ return ETH_ALEN;
+}
+
+static int netmap_mtu_get(pktio_entry_t *pktio_entry)
+{
+ return mtu_get_fd(pktio_entry->s.pkt_nm.sockfd, pktio_entry->s.name);
+}
+
+static int netmap_promisc_mode_set(pktio_entry_t *pktio_entry,
+ odp_bool_t enable)
+{
+ return promisc_mode_set_fd(pktio_entry->s.pkt_nm.sockfd,
+ pktio_entry->s.name, enable);
+}
+
+static int netmap_promisc_mode_get(pktio_entry_t *pktio_entry)
+{
+ return promisc_mode_get_fd(pktio_entry->s.pkt_nm.sockfd,
+ pktio_entry->s.name);
+}
+
+const pktio_if_ops_t netmap_pktio_ops = {
+ .init = NULL,
+ .term = NULL,
+ .open = netmap_open,
+ .close = netmap_close,
+ .start = NULL,
+ .stop = NULL,
+ .recv = netmap_recv,
+ .send = netmap_send,
+ .mtu_get = netmap_mtu_get,
+ .promisc_mode_set = netmap_promisc_mode_set,
+ .promisc_mode_get = netmap_promisc_mode_get,
+ .mac_get = netmap_mac_addr_get
+};
+
+#endif /* ODP_NETMAP */
diff --git a/platform/linux-generic/pktio/socket.c b/platform/linux-generic/pktio/socket.c
index 1ced69a..a95b9a8 100644
--- a/platform/linux-generic/pktio/socket.c
+++ b/platform/linux-generic/pktio/socket.c
@@ -91,6 +91,7 @@ int sendmmsg(int fd, struct mmsghdr *vmessages, unsigned int vlen, int flags)
/**
* ODP_PACKET_SOCKET_MMSG:
* ODP_PACKET_SOCKET_MMAP:
+ * ODP_PACKET_NETMAP:
*/
int mac_addr_get_fd(int fd, const char *name, unsigned char mac_dst[])
{
@@ -115,6 +116,7 @@ int mac_addr_get_fd(int fd, const char *name, unsigned char mac_dst[])
/*
* ODP_PACKET_SOCKET_MMSG:
* ODP_PACKET_SOCKET_MMAP:
+ * ODP_PACKET_NETMAP:
*/
int mtu_get_fd(int fd, const char *name)
{
@@ -133,6 +135,7 @@ int mtu_get_fd(int fd, const char *name)
/*
* ODP_PACKET_SOCKET_MMSG:
* ODP_PACKET_SOCKET_MMAP:
+ * ODP_PACKET_NETMAP:
*/
int promisc_mode_set_fd(int fd, const char *name, int enable)
{
@@ -162,6 +165,7 @@ int promisc_mode_set_fd(int fd, const char *name, int enable)
/*
* ODP_PACKET_SOCKET_MMSG:
* ODP_PACKET_SOCKET_MMAP:
+ * ODP_PACKET_NETMAP:
*/
int promisc_mode_get_fd(int fd, const char *name)
{
diff --git a/test/performance/odp_l2fwd.c b/test/performance/odp_l2fwd.c
index cb31544..812d47f 100644
--- a/test/performance/odp_l2fwd.c
+++ b/test/performance/odp_l2fwd.c
@@ -758,7 +758,8 @@ static void usage(char *progname)
" -a, --accuracy <number> Time in seconds get print statistics\n"
" (default is 1 second).\n"
" -h, --help Display help and exit.\n\n"
- " environment variables: ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
+ " environment variables: ODP_PKTIO_DISABLE_NETMAP\n"
+ " ODP_PKTIO_DISABLE_SOCKET_MMAP\n"
" ODP_PKTIO_DISABLE_SOCKET_MMSG\n"
" can be used to advanced pkt I/O selection for linux-generic\n"
"\n", NO_PATH(progname), NO_PATH(progname)