+#endif
+
+static int
+load_custom_xdp_prog(const char *prog_path, int if_index, struct bpf_map **map)
+{
+ int ret, prog_fd = -1;
+ struct bpf_object *obj;
+
+ ret = bpf_prog_load(prog_path, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
+ if (ret) {
+ AF_XDP_LOG(ERR, "Failed to load program %s\n", prog_path);
+ return ret;
+ }
+
+ /*
+ * The loaded program must provision for a map of xsks, such that some
+ * traffic can be redirected to userspace.
+ */
+ *map = bpf_object__find_map_by_name(obj, "xsks_map");
+ if (!*map) {
+ AF_XDP_LOG(ERR, "Failed to find xsks_map in %s\n", prog_path);
+ return -1;
+ }
+
+ /* Link the program with the given network device */
+ ret = bpf_set_link_xdp_fd(if_index, prog_fd,
+ XDP_FLAGS_UPDATE_IF_NOEXIST);
+ if (ret) {
+ AF_XDP_LOG(ERR, "Failed to set prog fd %d on interface\n",
+ prog_fd);
+ return -1;
+ }
+
+ AF_XDP_LOG(INFO, "Successfully loaded XDP program %s with fd %d\n",
+ prog_path, prog_fd);
+
+ return 0;
+}
+
+/* Detect support for busy polling through setsockopt(). */
+static int
+configure_preferred_busy_poll(struct pkt_rx_queue *rxq)
+{
+ int sock_opt = 1;
+ int fd = xsk_socket__fd(rxq->xsk);
+ int ret = 0;
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_PREFER_BUSY_POLL,
+ (void *)&sock_opt, sizeof(sock_opt));
+ if (ret < 0) {
+ AF_XDP_LOG(DEBUG, "Failed to set SO_PREFER_BUSY_POLL\n");
+ goto err_prefer;
+ }
+
+ sock_opt = ETH_AF_XDP_DFLT_BUSY_TIMEOUT;
+ ret = setsockopt(fd, SOL_SOCKET, SO_BUSY_POLL, (void *)&sock_opt,
+ sizeof(sock_opt));
+ if (ret < 0) {
+ AF_XDP_LOG(DEBUG, "Failed to set SO_BUSY_POLL\n");
+ goto err_timeout;
+ }
+
+ sock_opt = rxq->busy_budget;
+ ret = setsockopt(fd, SOL_SOCKET, SO_BUSY_POLL_BUDGET,
+ (void *)&sock_opt, sizeof(sock_opt));
+ if (ret < 0) {
+ AF_XDP_LOG(DEBUG, "Failed to set SO_BUSY_POLL_BUDGET\n");
+ } else {
+ AF_XDP_LOG(INFO, "Busy polling budget set to: %u\n",
+ rxq->busy_budget);
+ return 0;
+ }
+
+ /* setsockopt failure - attempt to restore xsk to default state and
+ * proceed without busy polling support.
+ */
+ sock_opt = 0;
+ ret = setsockopt(fd, SOL_SOCKET, SO_BUSY_POLL, (void *)&sock_opt,
+ sizeof(sock_opt));
+ if (ret < 0) {
+ AF_XDP_LOG(ERR, "Failed to unset SO_BUSY_POLL\n");
+ return -1;
+ }
+
+err_timeout:
+ sock_opt = 0;
+ ret = setsockopt(fd, SOL_SOCKET, SO_PREFER_BUSY_POLL,
+ (void *)&sock_opt, sizeof(sock_opt));
+ if (ret < 0) {
+ AF_XDP_LOG(ERR, "Failed to unset SO_PREFER_BUSY_POLL\n");
+ return -1;
+ }
+
+err_prefer:
+ rxq->busy_budget = 0;
+ return 0;
+}