cregit-Linux how code gets into the kernel

Release 4.10 samples/mic/mpssd/mpssd.c

/*
 * Intel MIC Platform Software Stack (MPSS)
 *
 * Copyright(c) 2013 Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * The full GNU General Public License is included in this distribution in
 * the file called "COPYING".
 *
 * Intel MIC User Space Tools.
 */


#define _GNU_SOURCE

#include <stdlib.h>
#include <fcntl.h>
#include <getopt.h>
#include <assert.h>
#include <unistd.h>
#include <stdbool.h>
#include <signal.h>
#include <poll.h>
#include <features.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <linux/virtio_ring.h>
#include <linux/virtio_net.h>
#include <linux/virtio_console.h>
#include <linux/virtio_blk.h>
#include <linux/version.h>
#include "mpssd.h"
#include <linux/mic_ioctl.h>
#include <linux/mic_common.h>
#include <tools/endian.h>

static void *init_mic(void *arg);


static FILE *logfp;

static struct mic_info mic_list;


#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))


#define min_t(type, x, y) ({                              \
                type __min1 = (x);                      \
                type __min2 = (y);                      \
                __min1 < __min2 ? __min1 : __min2; })

/* align addr on a size boundary - adjust address up/down if needed */

#define _ALIGN_DOWN(addr, size)  ((addr)&(~((size)-1)))

#define _ALIGN_UP(addr, size)    _ALIGN_DOWN(addr + size - 1, size)

/* align addr on a size boundary - adjust address up if needed */

#define _ALIGN(addr, size)     _ALIGN_UP(addr, size)

/* to align the pointer to the (next) page boundary */

#define PAGE_ALIGN(addr)        _ALIGN(addr, PAGE_SIZE)


#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))


#define GSO_ENABLED		1

#define MAX_GSO_SIZE		(64 * 1024)

#define ETH_H_LEN		14

#define MAX_NET_PKT_SIZE	(_ALIGN_UP(MAX_GSO_SIZE + ETH_H_LEN, 64))

#define MIC_DEVICE_PAGE_END	0x1000

#ifndef VIRTIO_NET_HDR_F_DATA_VALID

#define VIRTIO_NET_HDR_F_DATA_VALID	2	
/* Csum is valid */
#endif

static struct {
	
struct mic_device_desc dd;
	
struct mic_vqconfig vqconfig[2];
	

__u32 host_features, guest_acknowledgements;
	
struct virtio_console_config cons_config;
} 
virtcons_dev_page = {
	.dd = {
		.type = VIRTIO_ID_CONSOLE,
		.num_vq = ARRAY_SIZE(virtcons_dev_page.vqconfig),
		.feature_len = sizeof(virtcons_dev_page.host_features),
		.config_len = sizeof(virtcons_dev_page.cons_config),
        },
	.vqconfig[0] = {
		.num = htole16(MIC_VRING_ENTRIES),
        },
	.vqconfig[1] = {
		.num = htole16(MIC_VRING_ENTRIES),
        },
};

static struct {
	
struct mic_device_desc dd;
	
struct mic_vqconfig vqconfig[2];
	

__u32 host_features, guest_acknowledgements;
	
struct virtio_net_config net_config;
} 
virtnet_dev_page = {
	.dd = {
		.type = VIRTIO_ID_NET,
		.num_vq = ARRAY_SIZE(virtnet_dev_page.vqconfig),
		.feature_len = sizeof(virtnet_dev_page.host_features),
		.config_len = sizeof(virtnet_dev_page.net_config),
        },
	.vqconfig[0] = {
		.num = htole16(MIC_VRING_ENTRIES),
        },
	.vqconfig[1] = {
		.num = htole16(MIC_VRING_ENTRIES),
        },
#if GSO_ENABLED
	.host_features = htole32(
		1 << VIRTIO_NET_F_CSUM |
		1 << VIRTIO_NET_F_GSO |
		1 << VIRTIO_NET_F_GUEST_TSO4 |
		1 << VIRTIO_NET_F_GUEST_TSO6 |
		1 << VIRTIO_NET_F_GUEST_ECN),
#else
		.host_features = 0,
#endif
};


static const char *mic_config_dir = "/etc/mpss";

static const char *virtblk_backend = "VIRTBLK_BACKEND";
static struct {
	
struct mic_device_desc dd;
	
struct mic_vqconfig vqconfig[1];
	

__u32 host_features, guest_acknowledgements;
	
struct virtio_blk_config blk_config;
} 
virtblk_dev_page = {
	.dd = {
		.type = VIRTIO_ID_BLOCK,
		.num_vq = ARRAY_SIZE(virtblk_dev_page.vqconfig),
		.feature_len = sizeof(virtblk_dev_page.host_features),
		.config_len = sizeof(virtblk_dev_page.blk_config),
        },
	.vqconfig[0] = {
		.num = htole16(MIC_VRING_ENTRIES),
        },
	.host_features =
		htole32(1<<VIRTIO_BLK_F_SEG_MAX),
	.blk_config = {
		.seg_max = htole32(MIC_VRING_ENTRIES - 2),
		.capacity = htole64(0),
         }
};


static char *myname;


static int tap_configure(struct mic_info *mic, char *dev) { pid_t pid; char *ifargv[7]; char ipaddr[IFNAMSIZ]; int ret = 0; pid = fork(); if (pid == 0) { ifargv[0] = "ip"; ifargv[1] = "link"; ifargv[2] = "set"; ifargv[3] = dev; ifargv[4] = "up"; ifargv[5] = NULL; mpsslog("Configuring %s\n", dev); ret = execvp("ip", ifargv); if (ret < 0) { mpsslog("%s execvp failed errno %s\n", mic->name, strerror(errno)); return ret; } } if (pid < 0) { mpsslog("%s fork failed errno %s\n", mic->name, strerror(errno)); return ret; } ret = waitpid(pid, NULL, 0); if (ret < 0) { mpsslog("%s waitpid failed errno %s\n", mic->name, strerror(errno)); return ret; } snprintf(ipaddr, IFNAMSIZ, "172.31.%d.254/24", mic->id + 1); pid = fork(); if (pid == 0) { ifargv[0] = "ip"; ifargv[1] = "addr"; ifargv[2] = "add"; ifargv[3] = ipaddr; ifargv[4] = "dev"; ifargv[5] = dev; ifargv[6] = NULL; mpsslog("Configuring %s ipaddr %s\n", dev, ipaddr); ret = execvp("ip", ifargv); if (ret < 0) { mpsslog("%s execvp failed errno %s\n", mic->name, strerror(errno)); return ret; } } if (pid < 0) { mpsslog("%s fork failed errno %s\n", mic->name, strerror(errno)); return ret; } ret = waitpid(pid, NULL, 0); if (ret < 0) { mpsslog("%s waitpid failed errno %s\n", mic->name, strerror(errno)); return ret; } mpsslog("MIC name %s %s %d DONE!\n", mic->name, __func__, __LINE__); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama38899.49%150.00%
ashutosh dixitashutosh dixit20.51%150.00%
Total390100.00%2100.00%


static int tun_alloc(struct mic_info *mic, char *dev) { struct ifreq ifr; int fd, err; #if GSO_ENABLED unsigned offload; #endif fd = open("/dev/net/tun", O_RDWR); if (fd < 0) { mpsslog("Could not open /dev/net/tun %s\n", strerror(errno)); goto done; } memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR; if (*dev) strncpy(ifr.ifr_name, dev, IFNAMSIZ); err = ioctl(fd, TUNSETIFF, (void *)&ifr); if (err < 0) { mpsslog("%s %s %d TUNSETIFF failed %s\n", mic->name, __func__, __LINE__, strerror(errno)); close(fd); return err; } #if GSO_ENABLED offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | TUN_F_TSO_ECN; err = ioctl(fd, TUNSETOFFLOAD, offload); if (err < 0) { mpsslog("%s %s %d TUNSETOFFLOAD failed %s\n", mic->name, __func__, __LINE__, strerror(errno)); close(fd); return err; } #endif strcpy(dev, ifr.ifr_name); mpsslog("Created TAP %s\n", dev); done: return fd; }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama232100.00%1100.00%
Total232100.00%1100.00%

#define NET_FD_VIRTIO_NET 0 #define NET_FD_TUN 1 #define MAX_NET_FD 2
static void set_dp(struct mic_info *mic, int type, void *dp) { switch (type) { case VIRTIO_ID_CONSOLE: mic->mic_console.console_dp = dp; return; case VIRTIO_ID_NET: mic->mic_net.net_dp = dp; return; case VIRTIO_ID_BLOCK: mic->mic_virtblk.block_dp = dp; return; } mpsslog("%s %s %d not found\n", mic->name, __func__, type); assert(0); }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama78100.00%1100.00%
Total78100.00%1100.00%


static void *get_dp(struct mic_info *mic, int type) { switch (type) { case VIRTIO_ID_CONSOLE: return mic->mic_console.console_dp; case VIRTIO_ID_NET: return mic->mic_net.net_dp; case VIRTIO_ID_BLOCK: return mic->mic_virtblk.block_dp; } mpsslog("%s %s %d not found\n", mic->name, __func__, type); assert(0); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama72100.00%1100.00%
Total72100.00%1100.00%


static struct mic_device_desc *get_device_desc(struct mic_info *mic, int type) { struct mic_device_desc *d; int i; void *dp = get_dp(mic, type); for (i = sizeof(struct mic_bootparam); i < PAGE_SIZE; i += mic_total_desc_size(d)) { d = dp + i; /* End of list */ if (d->type == 0) break; if (d->type == -1) continue; mpsslog("%s %s d-> type %d d %p\n", mic->name, __func__, d->type, d); if (d->type == (__u8)type) return d; } mpsslog("%s %s %d not found\n", mic->name, __func__, type); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama13099.24%150.00%
ashutosh dixitashutosh dixit10.76%150.00%
Total131100.00%2100.00%

/* See comments in vhost.c for explanation of next_desc() */
static unsigned next_desc(struct vring_desc *desc) { unsigned int next; if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT)) return -1U; next = le16toh(desc->next); return next; }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama45100.00%1100.00%
Total45100.00%1100.00%

/* Sum up all the IOVEC length */
static ssize_t sum_iovec_len(struct mic_copy_desc *copy) { ssize_t sum = 0; unsigned int i; for (i = 0; i < copy->iovcnt; i++) sum += copy->iov[i].iov_len; return sum; }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama4897.96%150.00%
mahesh khanwalkarmahesh khanwalkar12.04%150.00%
Total49100.00%2100.00%


static inline void verify_out_len(struct mic_info *mic, struct mic_copy_desc *copy) { if (copy->out_len != sum_iovec_len(copy)) { mpsslog("%s %s %d BUG copy->out_len 0x%x len 0x%zx\n", mic->name, __func__, __LINE__, copy->out_len, sum_iovec_len(copy)); assert(copy->out_len == sum_iovec_len(copy)); } }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama6398.44%150.00%
sudeep duttsudeep dutt11.56%150.00%
Total64100.00%2100.00%

/* Display an iovec */
static void disp_iovec(struct mic_info *mic, struct mic_copy_desc *copy, const char *s, int line) { unsigned int i; for (i = 0; i < copy->iovcnt; i++) mpsslog("%s %s %d copy->iov[%d] addr %p len 0x%zx\n", mic->name, s, line, i, copy->iov[i].iov_base, copy->iov[i].iov_len); }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama7497.37%133.33%
mahesh khanwalkarmahesh khanwalkar11.32%133.33%
sudeep duttsudeep dutt11.32%133.33%
Total76100.00%3100.00%


static inline __u16 read_avail_idx(struct mic_vring *vr) { return ACCESS_ONCE(vr->info->avail_idx); }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama22100.00%1100.00%
Total22100.00%1100.00%


static inline void txrx_prepare(int type, bool tx, struct mic_vring *vr, struct mic_copy_desc *copy, ssize_t len) { copy->vr_idx = tx ? 0 : 1; copy->update_used = true; if (type == VIRTIO_ID_NET) copy->iov[1].iov_len = len - sizeof(struct virtio_net_hdr); else copy->iov[0].iov_len = len; }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama77100.00%1100.00%
Total77100.00%1100.00%

/* Central API which triggers the copies */
static int mic_virtio_copy(struct mic_info *mic, int fd, struct mic_vring *vr, struct mic_copy_desc *copy) { int ret; ret = ioctl(fd, MIC_VIRTIO_COPY_DESC, copy); if (ret) { mpsslog("%s %s %d errno %s ret %d\n", mic->name, __func__, __LINE__, strerror(errno), ret); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama67100.00%1100.00%
Total67100.00%1100.00%


static inline unsigned _vring_size(unsigned int num, unsigned long align) { return ((sizeof(struct vring_desc) * num + sizeof(__u16) * (3 + num) + align - 1) & ~(align - 1)) + sizeof(__u16) * 3 + sizeof(struct vring_used_elem) * num; }

Contributors

PersonTokensPropCommitsCommitProp
ashutosh dixitashutosh dixit65100.00%1100.00%
Total65100.00%1100.00%

/* * This initialization routine requires at least one * vring i.e. vr0. vr1 is optional. */
static void * init_vr(struct mic_info *mic, int fd, int type, struct mic_vring *vr0, struct mic_vring *vr1, int num_vq) { int vr_size; char *va; vr_size = PAGE_ALIGN(_vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN) + sizeof(struct _mic_vring_info)); va = mmap(NULL, MIC_DEVICE_PAGE_END + vr_size * num_vq, PROT_READ, MAP_SHARED, fd, 0); if (MAP_FAILED == va) { mpsslog("%s %s %d mmap failed errno %s\n", mic->name, __func__, __LINE__, strerror(errno)); goto done; } set_dp(mic, type, va); vr0->va = (struct mic_vring *)&va[MIC_DEVICE_PAGE_END]; vr0->info = vr0->va + _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN); vring_init(&vr0->vr, MIC_VRING_ENTRIES, vr0->va, MIC_VIRTIO_RING_ALIGN); mpsslog("%s %s vr0 %p vr0->info %p vr_size 0x%x vring 0x%x ", __func__, mic->name, vr0->va, vr0->info, vr_size, _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); mpsslog("magic 0x%x expected 0x%x\n", le32toh(vr0->info->magic), MIC_MAGIC + type); assert(le32toh(vr0->info->magic) == MIC_MAGIC + type); if (vr1) { vr1->va = (struct mic_vring *) &va[MIC_DEVICE_PAGE_END + vr_size]; vr1->info = vr1->va + _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN); vring_init(&vr1->vr, MIC_VRING_ENTRIES, vr1->va, MIC_VIRTIO_RING_ALIGN); mpsslog("%s %s vr1 %p vr1->info %p vr_size 0x%x vring 0x%x ", __func__, mic->name, vr1->va, vr1->info, vr_size, _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); mpsslog("magic 0x%x expected 0x%x\n", le32toh(vr1->info->magic), MIC_MAGIC + type + 1); assert(le32toh(vr1->info->magic) == MIC_MAGIC + type + 1); } done: return va; }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama33195.11%133.33%
ashutosh dixitashutosh dixit174.89%266.67%
Total348100.00%3100.00%


static int wait_for_card_driver(struct mic_info *mic, int fd, int type) { struct pollfd pollfd; int err; struct mic_device_desc *desc = get_device_desc(mic, type); __u8 prev_status; if (!desc) return -ENODEV; prev_status = desc->status; pollfd.fd = fd; mpsslog("%s %s Waiting .... desc-> type %d status 0x%x\n", mic->name, __func__, type, desc->status); while (1) { pollfd.events = POLLIN; pollfd.revents = 0; err = poll(&pollfd, 1, -1); if (err < 0) { mpsslog("%s %s poll failed %s\n", mic->name, __func__, strerror(errno)); continue; } if (pollfd.revents) { if (desc->status != prev_status) { mpsslog("%s %s Waiting... desc-> type %d " "status 0x%x\n", mic->name, __func__, type, desc->status); prev_status = desc->status; } if (desc->status & VIRTIO_CONFIG_S_DRIVER_OK) { mpsslog("%s %s poll.revents %d\n", mic->name, __func__, pollfd.revents); mpsslog("%s %s desc-> type %d status 0x%x\n", mic->name, __func__, type, desc->status); break; } } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama18181.90%150.00%
ashutosh dixitashutosh dixit4018.10%150.00%
Total221100.00%2100.00%

/* Spin till we have some descriptors */
static void spin_for_descriptors(struct mic_info *mic, struct mic_vring *vr) { __u16 avail_idx = read_avail_idx(vr); while (avail_idx == le16toh(ACCESS_ONCE(vr->vr.avail->idx))) { #ifdef DEBUG mpsslog("%s %s waiting for desc avail %d info_avail %d\n", mic->name, __func__, le16toh(vr->vr.avail->idx), vr->info->avail_idx); #endif sched_yield(); } }

Contributors

PersonTokensPropCommitsCommitProp
caz yokoyamacaz yokoyama80100.00%1100.00%
Total80100.00%1100.00%


static void * virtio_net(void *arg) { static __u8 vnet_hdr[2][sizeof(struct virtio_net_hdr)]; static __u8 vnet_buf[2][MAX_NET_PKT_SIZE] __attribute__ ((aligned(64))); struct iovec vnet_iov[2][2] = { { { .iov_base = vnet_hdr[0], .iov_len = sizeof(vnet_hdr[0]) }, { .iov_base = vnet_buf[0], .iov_len = sizeof(vnet_buf[0]) } }, { { .iov_base = vnet_hdr[1], .iov_len = sizeof(vnet_hdr[1]) }, { .iov_base = vnet_buf[1], .iov_len = sizeof(vnet_buf[1]) } }, }; struct iovec *iov0 = vnet_iov[0], *iov1 = vnet_iov[1]; struct mic_info *mic = (struct mic_info *)arg; char if_name[IFNAMSIZ]; struct pollfd net_poll[MAX_NET_FD]; struct mic_vring tx_vr, rx_vr; struct mic_copy_desc copy; struct mic_device_desc *desc; int err; snprintf(if_name, IFNAMSIZ, "mic%d", mic->id); mic->mic_net.tap_fd = tun_alloc(mic, if_name); if (mic->mic_net.tap_fd < 0) goto done; if (tap_configure(mic, if_name)) goto done; mpsslog("MIC name %s id %d\n", mic->name, mic->id); net_poll[NET_FD_VIRTIO_NET].fd = mic->mic_net.virtio_net_fd; net_poll[NET_FD_VIRTIO_NET].events = POLLIN;