| /* | 
 |  * 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 READ_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; | 
 | } | 
 |  | 
 | 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; | 
 | } | 
 |  | 
 | #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); | 
 | } | 
 |  | 
 | 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; | 
 | } | 
 |  | 
 | 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; | 
 | } | 
 |  | 
 | /* 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; | 
 | } | 
 |  | 
 | /* 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; | 
 | } | 
 |  | 
 | 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)); | 
 | 	} | 
 | } | 
 |  | 
 | /* 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); | 
 | } | 
 |  | 
 | static inline __u16 read_avail_idx(struct mic_vring *vr) | 
 | { | 
 | 	return READ_ONCE(vr->info->avail_idx); | 
 | } | 
 |  | 
 | 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; | 
 | } | 
 |  | 
 | /* 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; | 
 | } | 
 |  | 
 | 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; | 
 | } | 
 |  | 
 | /* | 
 |  * 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; | 
 | } | 
 |  | 
 | 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; | 
 | } | 
 |  | 
 | /* 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(READ_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(); | 
 | 	} | 
 | } | 
 |  | 
 | 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; | 
 | 	net_poll[NET_FD_TUN].fd = mic->mic_net.tap_fd; | 
 | 	net_poll[NET_FD_TUN].events = POLLIN; | 
 |  | 
 | 	if (MAP_FAILED == init_vr(mic, mic->mic_net.virtio_net_fd, | 
 | 				  VIRTIO_ID_NET, &tx_vr, &rx_vr, | 
 | 		virtnet_dev_page.dd.num_vq)) { | 
 | 		mpsslog("%s init_vr failed %s\n", | 
 | 			mic->name, strerror(errno)); | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	copy.iovcnt = 2; | 
 | 	desc = get_device_desc(mic, VIRTIO_ID_NET); | 
 |  | 
 | 	while (1) { | 
 | 		ssize_t len; | 
 |  | 
 | 		net_poll[NET_FD_VIRTIO_NET].revents = 0; | 
 | 		net_poll[NET_FD_TUN].revents = 0; | 
 |  | 
 | 		/* Start polling for data from tap and virtio net */ | 
 | 		err = poll(net_poll, 2, -1); | 
 | 		if (err < 0) { | 
 | 			mpsslog("%s poll failed %s\n", | 
 | 				__func__, strerror(errno)); | 
 | 			continue; | 
 | 		} | 
 | 		if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) { | 
 | 			err = wait_for_card_driver(mic, | 
 | 						   mic->mic_net.virtio_net_fd, | 
 | 						   VIRTIO_ID_NET); | 
 | 			if (err) { | 
 | 				mpsslog("%s %s %d Exiting...\n", | 
 | 					mic->name, __func__, __LINE__); | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 		/* | 
 | 		 * Check if there is data to be read from TUN and write to | 
 | 		 * virtio net fd if there is. | 
 | 		 */ | 
 | 		if (net_poll[NET_FD_TUN].revents & POLLIN) { | 
 | 			copy.iov = iov0; | 
 | 			len = readv(net_poll[NET_FD_TUN].fd, | 
 | 				copy.iov, copy.iovcnt); | 
 | 			if (len > 0) { | 
 | 				struct virtio_net_hdr *hdr | 
 | 					= (struct virtio_net_hdr *)vnet_hdr[0]; | 
 |  | 
 | 				/* Disable checksums on the card since we are on | 
 | 				   a reliable PCIe link */ | 
 | 				hdr->flags |= VIRTIO_NET_HDR_F_DATA_VALID; | 
 | #ifdef DEBUG | 
 | 				mpsslog("%s %s %d hdr->flags 0x%x ", mic->name, | 
 | 					__func__, __LINE__, hdr->flags); | 
 | 				mpsslog("copy.out_len %d hdr->gso_type 0x%x\n", | 
 | 					copy.out_len, hdr->gso_type); | 
 | #endif | 
 | #ifdef DEBUG | 
 | 				disp_iovec(mic, copy, __func__, __LINE__); | 
 | 				mpsslog("%s %s %d read from tap 0x%lx\n", | 
 | 					mic->name, __func__, __LINE__, | 
 | 					len); | 
 | #endif | 
 | 				spin_for_descriptors(mic, &tx_vr); | 
 | 				txrx_prepare(VIRTIO_ID_NET, 1, &tx_vr, ©, | 
 | 					     len); | 
 |  | 
 | 				err = mic_virtio_copy(mic, | 
 | 					mic->mic_net.virtio_net_fd, &tx_vr, | 
 | 					©); | 
 | 				if (err < 0) { | 
 | 					mpsslog("%s %s %d mic_virtio_copy %s\n", | 
 | 						mic->name, __func__, __LINE__, | 
 | 						strerror(errno)); | 
 | 				} | 
 | 				if (!err) | 
 | 					verify_out_len(mic, ©); | 
 | #ifdef DEBUG | 
 | 				disp_iovec(mic, copy, __func__, __LINE__); | 
 | 				mpsslog("%s %s %d wrote to net 0x%lx\n", | 
 | 					mic->name, __func__, __LINE__, | 
 | 					sum_iovec_len(©)); | 
 | #endif | 
 | 				/* Reinitialize IOV for next run */ | 
 | 				iov0[1].iov_len = MAX_NET_PKT_SIZE; | 
 | 			} else if (len < 0) { | 
 | 				disp_iovec(mic, ©, __func__, __LINE__); | 
 | 				mpsslog("%s %s %d read failed %s ", mic->name, | 
 | 					__func__, __LINE__, strerror(errno)); | 
 | 				mpsslog("cnt %d sum %zd\n", | 
 | 					copy.iovcnt, sum_iovec_len(©)); | 
 | 			} | 
 | 		} | 
 |  | 
 | 		/* | 
 | 		 * Check if there is data to be read from virtio net and | 
 | 		 * write to TUN if there is. | 
 | 		 */ | 
 | 		if (net_poll[NET_FD_VIRTIO_NET].revents & POLLIN) { | 
 | 			while (rx_vr.info->avail_idx != | 
 | 				le16toh(rx_vr.vr.avail->idx)) { | 
 | 				copy.iov = iov1; | 
 | 				txrx_prepare(VIRTIO_ID_NET, 0, &rx_vr, ©, | 
 | 					     MAX_NET_PKT_SIZE | 
 | 					+ sizeof(struct virtio_net_hdr)); | 
 |  | 
 | 				err = mic_virtio_copy(mic, | 
 | 					mic->mic_net.virtio_net_fd, &rx_vr, | 
 | 					©); | 
 | 				if (!err) { | 
 | #ifdef DEBUG | 
 | 					struct virtio_net_hdr *hdr | 
 | 						= (struct virtio_net_hdr *) | 
 | 							vnet_hdr[1]; | 
 |  | 
 | 					mpsslog("%s %s %d hdr->flags 0x%x, ", | 
 | 						mic->name, __func__, __LINE__, | 
 | 						hdr->flags); | 
 | 					mpsslog("out_len %d gso_type 0x%x\n", | 
 | 						copy.out_len, | 
 | 						hdr->gso_type); | 
 | #endif | 
 | 					/* Set the correct output iov_len */ | 
 | 					iov1[1].iov_len = copy.out_len - | 
 | 						sizeof(struct virtio_net_hdr); | 
 | 					verify_out_len(mic, ©); | 
 | #ifdef DEBUG | 
 | 					disp_iovec(mic, copy, __func__, | 
 | 						   __LINE__); | 
 | 					mpsslog("%s %s %d ", | 
 | 						mic->name, __func__, __LINE__); | 
 | 					mpsslog("read from net 0x%lx\n", | 
 | 						sum_iovec_len(copy)); | 
 | #endif | 
 | 					len = writev(net_poll[NET_FD_TUN].fd, | 
 | 						copy.iov, copy.iovcnt); | 
 | 					if (len != sum_iovec_len(©)) { | 
 | 						mpsslog("Tun write failed %s ", | 
 | 							strerror(errno)); | 
 | 						mpsslog("len 0x%zx ", len); | 
 | 						mpsslog("read_len 0x%zx\n", | 
 | 							sum_iovec_len(©)); | 
 | 					} else { | 
 | #ifdef DEBUG | 
 | 						disp_iovec(mic, ©, __func__, | 
 | 							   __LINE__); | 
 | 						mpsslog("%s %s %d ", | 
 | 							mic->name, __func__, | 
 | 							__LINE__); | 
 | 						mpsslog("wrote to tap 0x%lx\n", | 
 | 							len); | 
 | #endif | 
 | 					} | 
 | 				} else { | 
 | 					mpsslog("%s %s %d mic_virtio_copy %s\n", | 
 | 						mic->name, __func__, __LINE__, | 
 | 						strerror(errno)); | 
 | 					break; | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		if (net_poll[NET_FD_VIRTIO_NET].revents & POLLERR) | 
 | 			mpsslog("%s: %s: POLLERR\n", __func__, mic->name); | 
 | 	} | 
 | done: | 
 | 	pthread_exit(NULL); | 
 | } | 
 |  | 
 | /* virtio_console */ | 
 | #define VIRTIO_CONSOLE_FD 0 | 
 | #define MONITOR_FD (VIRTIO_CONSOLE_FD + 1) | 
 | #define MAX_CONSOLE_FD (MONITOR_FD + 1)  /* must be the last one + 1 */ | 
 | #define MAX_BUFFER_SIZE PAGE_SIZE | 
 |  | 
 | static void * | 
 | virtio_console(void *arg) | 
 | { | 
 | 	static __u8 vcons_buf[2][PAGE_SIZE]; | 
 | 	struct iovec vcons_iov[2] = { | 
 | 		{ .iov_base = vcons_buf[0], .iov_len = sizeof(vcons_buf[0]) }, | 
 | 		{ .iov_base = vcons_buf[1], .iov_len = sizeof(vcons_buf[1]) }, | 
 | 	}; | 
 | 	struct iovec *iov0 = &vcons_iov[0], *iov1 = &vcons_iov[1]; | 
 | 	struct mic_info *mic = (struct mic_info *)arg; | 
 | 	int err; | 
 | 	struct pollfd console_poll[MAX_CONSOLE_FD]; | 
 | 	int pty_fd; | 
 | 	char *pts_name; | 
 | 	ssize_t len; | 
 | 	struct mic_vring tx_vr, rx_vr; | 
 | 	struct mic_copy_desc copy; | 
 | 	struct mic_device_desc *desc; | 
 |  | 
 | 	pty_fd = posix_openpt(O_RDWR); | 
 | 	if (pty_fd < 0) { | 
 | 		mpsslog("can't open a pseudoterminal master device: %s\n", | 
 | 			strerror(errno)); | 
 | 		goto _return; | 
 | 	} | 
 | 	pts_name = ptsname(pty_fd); | 
 | 	if (pts_name == NULL) { | 
 | 		mpsslog("can't get pts name\n"); | 
 | 		goto _close_pty; | 
 | 	} | 
 | 	printf("%s console message goes to %s\n", mic->name, pts_name); | 
 | 	mpsslog("%s console message goes to %s\n", mic->name, pts_name); | 
 | 	err = grantpt(pty_fd); | 
 | 	if (err < 0) { | 
 | 		mpsslog("can't grant access: %s %s\n", | 
 | 			pts_name, strerror(errno)); | 
 | 		goto _close_pty; | 
 | 	} | 
 | 	err = unlockpt(pty_fd); | 
 | 	if (err < 0) { | 
 | 		mpsslog("can't unlock a pseudoterminal: %s %s\n", | 
 | 			pts_name, strerror(errno)); | 
 | 		goto _close_pty; | 
 | 	} | 
 | 	console_poll[MONITOR_FD].fd = pty_fd; | 
 | 	console_poll[MONITOR_FD].events = POLLIN; | 
 |  | 
 | 	console_poll[VIRTIO_CONSOLE_FD].fd = mic->mic_console.virtio_console_fd; | 
 | 	console_poll[VIRTIO_CONSOLE_FD].events = POLLIN; | 
 |  | 
 | 	if (MAP_FAILED == init_vr(mic, mic->mic_console.virtio_console_fd, | 
 | 				  VIRTIO_ID_CONSOLE, &tx_vr, &rx_vr, | 
 | 		virtcons_dev_page.dd.num_vq)) { | 
 | 		mpsslog("%s init_vr failed %s\n", | 
 | 			mic->name, strerror(errno)); | 
 | 		goto _close_pty; | 
 | 	} | 
 |  | 
 | 	copy.iovcnt = 1; | 
 | 	desc = get_device_desc(mic, VIRTIO_ID_CONSOLE); | 
 |  | 
 | 	for (;;) { | 
 | 		console_poll[MONITOR_FD].revents = 0; | 
 | 		console_poll[VIRTIO_CONSOLE_FD].revents = 0; | 
 | 		err = poll(console_poll, MAX_CONSOLE_FD, -1); | 
 | 		if (err < 0) { | 
 | 			mpsslog("%s %d: poll failed: %s\n", __func__, __LINE__, | 
 | 				strerror(errno)); | 
 | 			continue; | 
 | 		} | 
 | 		if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) { | 
 | 			err = wait_for_card_driver(mic, | 
 | 					mic->mic_console.virtio_console_fd, | 
 | 					VIRTIO_ID_CONSOLE); | 
 | 			if (err) { | 
 | 				mpsslog("%s %s %d Exiting...\n", | 
 | 					mic->name, __func__, __LINE__); | 
 | 				break; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		if (console_poll[MONITOR_FD].revents & POLLIN) { | 
 | 			copy.iov = iov0; | 
 | 			len = readv(pty_fd, copy.iov, copy.iovcnt); | 
 | 			if (len > 0) { | 
 | #ifdef DEBUG | 
 | 				disp_iovec(mic, copy, __func__, __LINE__); | 
 | 				mpsslog("%s %s %d read from tap 0x%lx\n", | 
 | 					mic->name, __func__, __LINE__, | 
 | 					len); | 
 | #endif | 
 | 				spin_for_descriptors(mic, &tx_vr); | 
 | 				txrx_prepare(VIRTIO_ID_CONSOLE, 1, &tx_vr, | 
 | 					     ©, len); | 
 |  | 
 | 				err = mic_virtio_copy(mic, | 
 | 					mic->mic_console.virtio_console_fd, | 
 | 					&tx_vr, ©); | 
 | 				if (err < 0) { | 
 | 					mpsslog("%s %s %d mic_virtio_copy %s\n", | 
 | 						mic->name, __func__, __LINE__, | 
 | 						strerror(errno)); | 
 | 				} | 
 | 				if (!err) | 
 | 					verify_out_len(mic, ©); | 
 | #ifdef DEBUG | 
 | 				disp_iovec(mic, copy, __func__, __LINE__); | 
 | 				mpsslog("%s %s %d wrote to net 0x%lx\n", | 
 | 					mic->name, __func__, __LINE__, | 
 | 					sum_iovec_len(copy)); | 
 | #endif | 
 | 				/* Reinitialize IOV for next run */ | 
 | 				iov0->iov_len = PAGE_SIZE; | 
 | 			} else if (len < 0) { | 
 | 				disp_iovec(mic, ©, __func__, __LINE__); | 
 | 				mpsslog("%s %s %d read failed %s ", | 
 | 					mic->name, __func__, __LINE__, | 
 | 					strerror(errno)); | 
 | 				mpsslog("cnt %d sum %zd\n", | 
 | 					copy.iovcnt, sum_iovec_len(©)); | 
 | 			} | 
 | 		} | 
 |  | 
 | 		if (console_poll[VIRTIO_CONSOLE_FD].revents & POLLIN) { | 
 | 			while (rx_vr.info->avail_idx != | 
 | 				le16toh(rx_vr.vr.avail->idx)) { | 
 | 				copy.iov = iov1; | 
 | 				txrx_prepare(VIRTIO_ID_CONSOLE, 0, &rx_vr, | 
 | 					     ©, PAGE_SIZE); | 
 |  | 
 | 				err = mic_virtio_copy(mic, | 
 | 					mic->mic_console.virtio_console_fd, | 
 | 					&rx_vr, ©); | 
 | 				if (!err) { | 
 | 					/* Set the correct output iov_len */ | 
 | 					iov1->iov_len = copy.out_len; | 
 | 					verify_out_len(mic, ©); | 
 | #ifdef DEBUG | 
 | 					disp_iovec(mic, copy, __func__, | 
 | 						   __LINE__); | 
 | 					mpsslog("%s %s %d ", | 
 | 						mic->name, __func__, __LINE__); | 
 | 					mpsslog("read from net 0x%lx\n", | 
 | 						sum_iovec_len(copy)); | 
 | #endif | 
 | 					len = writev(pty_fd, | 
 | 						copy.iov, copy.iovcnt); | 
 | 					if (len != sum_iovec_len(©)) { | 
 | 						mpsslog("Tun write failed %s ", | 
 | 							strerror(errno)); | 
 | 						mpsslog("len 0x%zx ", len); | 
 | 						mpsslog("read_len 0x%zx\n", | 
 | 							sum_iovec_len(©)); | 
 | 					} else { | 
 | #ifdef DEBUG | 
 | 						disp_iovec(mic, copy, __func__, | 
 | 							   __LINE__); | 
 | 						mpsslog("%s %s %d ", | 
 | 							mic->name, __func__, | 
 | 							__LINE__); | 
 | 						mpsslog("wrote to tap 0x%lx\n", | 
 | 							len); | 
 | #endif | 
 | 					} | 
 | 				} else { | 
 | 					mpsslog("%s %s %d mic_virtio_copy %s\n", | 
 | 						mic->name, __func__, __LINE__, | 
 | 						strerror(errno)); | 
 | 					break; | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		if (console_poll[NET_FD_VIRTIO_NET].revents & POLLERR) | 
 | 			mpsslog("%s: %s: POLLERR\n", __func__, mic->name); | 
 | 	} | 
 | _close_pty: | 
 | 	close(pty_fd); | 
 | _return: | 
 | 	pthread_exit(NULL); | 
 | } | 
 |  | 
 | static void | 
 | add_virtio_device(struct mic_info *mic, struct mic_device_desc *dd) | 
 | { | 
 | 	char path[PATH_MAX]; | 
 | 	int fd, err; | 
 |  | 
 | 	snprintf(path, PATH_MAX, "/dev/vop_virtio%d", mic->id); | 
 | 	fd = open(path, O_RDWR); | 
 | 	if (fd < 0) { | 
 | 		mpsslog("Could not open %s %s\n", path, strerror(errno)); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	err = ioctl(fd, MIC_VIRTIO_ADD_DEVICE, dd); | 
 | 	if (err < 0) { | 
 | 		mpsslog("Could not add %d %s\n", dd->type, strerror(errno)); | 
 | 		close(fd); | 
 | 		return; | 
 | 	} | 
 | 	switch (dd->type) { | 
 | 	case VIRTIO_ID_NET: | 
 | 		mic->mic_net.virtio_net_fd = fd; | 
 | 		mpsslog("Added VIRTIO_ID_NET for %s\n", mic->name); | 
 | 		break; | 
 | 	case VIRTIO_ID_CONSOLE: | 
 | 		mic->mic_console.virtio_console_fd = fd; | 
 | 		mpsslog("Added VIRTIO_ID_CONSOLE for %s\n", mic->name); | 
 | 		break; | 
 | 	case VIRTIO_ID_BLOCK: | 
 | 		mic->mic_virtblk.virtio_block_fd = fd; | 
 | 		mpsslog("Added VIRTIO_ID_BLOCK for %s\n", mic->name); | 
 | 		break; | 
 | 	} | 
 | } | 
 |  | 
 | static bool | 
 | set_backend_file(struct mic_info *mic) | 
 | { | 
 | 	FILE *config; | 
 | 	char buff[PATH_MAX], *line, *evv, *p; | 
 |  | 
 | 	snprintf(buff, PATH_MAX, "%s/mpssd%03d.conf", mic_config_dir, mic->id); | 
 | 	config = fopen(buff, "r"); | 
 | 	if (config == NULL) | 
 | 		return false; | 
 | 	do {  /* look for "virtblk_backend=XXXX" */ | 
 | 		line = fgets(buff, PATH_MAX, config); | 
 | 		if (line == NULL) | 
 | 			break; | 
 | 		if (*line == '#') | 
 | 			continue; | 
 | 		p = strchr(line, '\n'); | 
 | 		if (p) | 
 | 			*p = '\0'; | 
 | 	} while (strncmp(line, virtblk_backend, strlen(virtblk_backend)) != 0); | 
 | 	fclose(config); | 
 | 	if (line == NULL) | 
 | 		return false; | 
 | 	evv = strchr(line, '='); | 
 | 	if (evv == NULL) | 
 | 		return false; | 
 | 	mic->mic_virtblk.backend_file = malloc(strlen(evv) + 1); | 
 | 	if (mic->mic_virtblk.backend_file == NULL) { | 
 | 		mpsslog("%s %d can't allocate memory\n", mic->name, mic->id); | 
 | 		return false; | 
 | 	} | 
 | 	strcpy(mic->mic_virtblk.backend_file, evv + 1); | 
 | 	return true; | 
 | } | 
 |  | 
 | #define SECTOR_SIZE 512 | 
 | static bool | 
 | set_backend_size(struct mic_info *mic) | 
 | { | 
 | 	mic->mic_virtblk.backend_size = lseek(mic->mic_virtblk.backend, 0, | 
 | 		SEEK_END); | 
 | 	if (mic->mic_virtblk.backend_size < 0) { | 
 | 		mpsslog("%s: can't seek: %s\n", | 
 | 			mic->name, mic->mic_virtblk.backend_file); | 
 | 		return false; | 
 | 	} | 
 | 	virtblk_dev_page.blk_config.capacity = | 
 | 		mic->mic_virtblk.backend_size / SECTOR_SIZE; | 
 | 	if ((mic->mic_virtblk.backend_size % SECTOR_SIZE) != 0) | 
 | 		virtblk_dev_page.blk_config.capacity++; | 
 |  | 
 | 	virtblk_dev_page.blk_config.capacity = | 
 | 		htole64(virtblk_dev_page.blk_config.capacity); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | static bool | 
 | open_backend(struct mic_info *mic) | 
 | { | 
 | 	if (!set_backend_file(mic)) | 
 | 		goto _error_exit; | 
 | 	mic->mic_virtblk.backend = open(mic->mic_virtblk.backend_file, O_RDWR); | 
 | 	if (mic->mic_virtblk.backend < 0) { | 
 | 		mpsslog("%s: can't open: %s\n", mic->name, | 
 | 			mic->mic_virtblk.backend_file); | 
 | 		goto _error_free; | 
 | 	} | 
 | 	if (!set_backend_size(mic)) | 
 | 		goto _error_close; | 
 | 	mic->mic_virtblk.backend_addr = mmap(NULL, | 
 | 		mic->mic_virtblk.backend_size, | 
 | 		PROT_READ|PROT_WRITE, MAP_SHARED, | 
 | 		mic->mic_virtblk.backend, 0L); | 
 | 	if (mic->mic_virtblk.backend_addr == MAP_FAILED) { | 
 | 		mpsslog("%s: can't map: %s %s\n", | 
 | 			mic->name, mic->mic_virtblk.backend_file, | 
 | 			strerror(errno)); | 
 | 		goto _error_close; | 
 | 	} | 
 | 	return true; | 
 |  | 
 |  _error_close: | 
 | 	close(mic->mic_virtblk.backend); | 
 |  _error_free: | 
 | 	free(mic->mic_virtblk.backend_file); | 
 |  _error_exit: | 
 | 	return false; | 
 | } | 
 |  | 
 | static void | 
 | close_backend(struct mic_info *mic) | 
 | { | 
 | 	munmap(mic->mic_virtblk.backend_addr, mic->mic_virtblk.backend_size); | 
 | 	close(mic->mic_virtblk.backend); | 
 | 	free(mic->mic_virtblk.backend_file); | 
 | } | 
 |  | 
 | static bool | 
 | start_virtblk(struct mic_info *mic, struct mic_vring *vring) | 
 | { | 
 | 	if (((unsigned long)&virtblk_dev_page.blk_config % 8) != 0) { | 
 | 		mpsslog("%s: blk_config is not 8 byte aligned.\n", | 
 | 			mic->name); | 
 | 		return false; | 
 | 	} | 
 | 	add_virtio_device(mic, &virtblk_dev_page.dd); | 
 | 	if (MAP_FAILED == init_vr(mic, mic->mic_virtblk.virtio_block_fd, | 
 | 				  VIRTIO_ID_BLOCK, vring, NULL, | 
 | 				  virtblk_dev_page.dd.num_vq)) { | 
 | 		mpsslog("%s init_vr failed %s\n", | 
 | 			mic->name, strerror(errno)); | 
 | 		return false; | 
 | 	} | 
 | 	return true; | 
 | } | 
 |  | 
 | static void | 
 | stop_virtblk(struct mic_info *mic) | 
 | { | 
 | 	int vr_size, ret; | 
 |  | 
 | 	vr_size = PAGE_ALIGN(_vring_size(MIC_VRING_ENTRIES, | 
 | 					 MIC_VIRTIO_RING_ALIGN) + | 
 | 			     sizeof(struct _mic_vring_info)); | 
 | 	ret = munmap(mic->mic_virtblk.block_dp, | 
 | 		MIC_DEVICE_PAGE_END + vr_size * virtblk_dev_page.dd.num_vq); | 
 | 	if (ret < 0) | 
 | 		mpsslog("%s munmap errno %d\n", mic->name, errno); | 
 | 	close(mic->mic_virtblk.virtio_block_fd); | 
 | } | 
 |  | 
 | static __u8 | 
 | header_error_check(struct vring_desc *desc) | 
 | { | 
 | 	if (le32toh(desc->len) != sizeof(struct virtio_blk_outhdr)) { | 
 | 		mpsslog("%s() %d: length is not sizeof(virtio_blk_outhd)\n", | 
 | 			__func__, __LINE__); | 
 | 		return -EIO; | 
 | 	} | 
 | 	if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT)) { | 
 | 		mpsslog("%s() %d: alone\n", | 
 | 			__func__, __LINE__); | 
 | 		return -EIO; | 
 | 	} | 
 | 	if (le16toh(desc->flags) & VRING_DESC_F_WRITE) { | 
 | 		mpsslog("%s() %d: not read\n", | 
 | 			__func__, __LINE__); | 
 | 		return -EIO; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int | 
 | read_header(int fd, struct virtio_blk_outhdr *hdr, __u32 desc_idx) | 
 | { | 
 | 	struct iovec iovec; | 
 | 	struct mic_copy_desc copy; | 
 |  | 
 | 	iovec.iov_len = sizeof(*hdr); | 
 | 	iovec.iov_base = hdr; | 
 | 	copy.iov = &iovec; | 
 | 	copy.iovcnt = 1; | 
 | 	copy.vr_idx = 0;  /* only one vring on virtio_block */ | 
 | 	copy.update_used = false;  /* do not update used index */ | 
 | 	return ioctl(fd, MIC_VIRTIO_COPY_DESC, ©); | 
 | } | 
 |  | 
 | static int | 
 | transfer_blocks(int fd, struct iovec *iovec, __u32 iovcnt) | 
 | { | 
 | 	struct mic_copy_desc copy; | 
 |  | 
 | 	copy.iov = iovec; | 
 | 	copy.iovcnt = iovcnt; | 
 | 	copy.vr_idx = 0;  /* only one vring on virtio_block */ | 
 | 	copy.update_used = false;  /* do not update used index */ | 
 | 	return ioctl(fd, MIC_VIRTIO_COPY_DESC, ©); | 
 | } | 
 |  | 
 | static __u8 | 
 | status_error_check(struct vring_desc *desc) | 
 | { | 
 | 	if (le32toh(desc->len) != sizeof(__u8)) { | 
 | 		mpsslog("%s() %d: length is not sizeof(status)\n", | 
 | 			__func__, __LINE__); | 
 | 		return -EIO; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int | 
 | write_status(int fd, __u8 *status) | 
 | { | 
 | 	struct iovec iovec; | 
 | 	struct mic_copy_desc copy; | 
 |  | 
 | 	iovec.iov_base = status; | 
 | 	iovec.iov_len = sizeof(*status); | 
 | 	copy.iov = &iovec; | 
 | 	copy.iovcnt = 1; | 
 | 	copy.vr_idx = 0;  /* only one vring on virtio_block */ | 
 | 	copy.update_used = true; /* Update used index */ | 
 | 	return ioctl(fd, MIC_VIRTIO_COPY_DESC, ©); | 
 | } | 
 |  | 
 | #ifndef VIRTIO_BLK_T_GET_ID | 
 | #define VIRTIO_BLK_T_GET_ID    8 | 
 | #endif | 
 |  | 
 | static void * | 
 | virtio_block(void *arg) | 
 | { | 
 | 	struct mic_info *mic = (struct mic_info *)arg; | 
 | 	int ret; | 
 | 	struct pollfd block_poll; | 
 | 	struct mic_vring vring; | 
 | 	__u16 avail_idx; | 
 | 	__u32 desc_idx; | 
 | 	struct vring_desc *desc; | 
 | 	struct iovec *iovec, *piov; | 
 | 	__u8 status; | 
 | 	__u32 buffer_desc_idx; | 
 | 	struct virtio_blk_outhdr hdr; | 
 | 	void *fos; | 
 |  | 
 | 	for (;;) {  /* forever */ | 
 | 		if (!open_backend(mic)) { /* No virtblk */ | 
 | 			for (mic->mic_virtblk.signaled = 0; | 
 | 				!mic->mic_virtblk.signaled;) | 
 | 				sleep(1); | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		/* backend file is specified. */ | 
 | 		if (!start_virtblk(mic, &vring)) | 
 | 			goto _close_backend; | 
 | 		iovec = malloc(sizeof(*iovec) * | 
 | 			le32toh(virtblk_dev_page.blk_config.seg_max)); | 
 | 		if (!iovec) { | 
 | 			mpsslog("%s: can't alloc iovec: %s\n", | 
 | 				mic->name, strerror(ENOMEM)); | 
 | 			goto _stop_virtblk; | 
 | 		} | 
 |  | 
 | 		block_poll.fd = mic->mic_virtblk.virtio_block_fd; | 
 | 		block_poll.events = POLLIN; | 
 | 		for (mic->mic_virtblk.signaled = 0; | 
 | 		     !mic->mic_virtblk.signaled;) { | 
 | 			block_poll.revents = 0; | 
 | 					/* timeout in 1 sec to see signaled */ | 
 | 			ret = poll(&block_poll, 1, 1000); | 
 | 			if (ret < 0) { | 
 | 				mpsslog("%s %d: poll failed: %s\n", | 
 | 					__func__, __LINE__, | 
 | 					strerror(errno)); | 
 | 				continue; | 
 | 			} | 
 |  | 
 | 			if (!(block_poll.revents & POLLIN)) { | 
 | #ifdef DEBUG | 
 | 				mpsslog("%s %d: block_poll.revents=0x%x\n", | 
 | 					__func__, __LINE__, block_poll.revents); | 
 | #endif | 
 | 				continue; | 
 | 			} | 
 |  | 
 | 			/* POLLIN */ | 
 | 			while (vring.info->avail_idx != | 
 | 				le16toh(vring.vr.avail->idx)) { | 
 | 				/* read header element */ | 
 | 				avail_idx = | 
 | 					vring.info->avail_idx & | 
 | 					(vring.vr.num - 1); | 
 | 				desc_idx = le16toh( | 
 | 					vring.vr.avail->ring[avail_idx]); | 
 | 				desc = &vring.vr.desc[desc_idx]; | 
 | #ifdef DEBUG | 
 | 				mpsslog("%s() %d: avail_idx=%d ", | 
 | 					__func__, __LINE__, | 
 | 					vring.info->avail_idx); | 
 | 				mpsslog("vring.vr.num=%d desc=%p\n", | 
 | 					vring.vr.num, desc); | 
 | #endif | 
 | 				status = header_error_check(desc); | 
 | 				ret = read_header( | 
 | 					mic->mic_virtblk.virtio_block_fd, | 
 | 					&hdr, desc_idx); | 
 | 				if (ret < 0) { | 
 | 					mpsslog("%s() %d %s: ret=%d %s\n", | 
 | 						__func__, __LINE__, | 
 | 						mic->name, ret, | 
 | 						strerror(errno)); | 
 | 					break; | 
 | 				} | 
 | 				/* buffer element */ | 
 | 				piov = iovec; | 
 | 				status = 0; | 
 | 				fos = mic->mic_virtblk.backend_addr + | 
 | 					(hdr.sector * SECTOR_SIZE); | 
 | 				buffer_desc_idx = next_desc(desc); | 
 | 				desc_idx = buffer_desc_idx; | 
 | 				for (desc = &vring.vr.desc[buffer_desc_idx]; | 
 | 				     desc->flags & VRING_DESC_F_NEXT; | 
 | 				     desc_idx = next_desc(desc), | 
 | 					     desc = &vring.vr.desc[desc_idx]) { | 
 | 					piov->iov_len = desc->len; | 
 | 					piov->iov_base = fos; | 
 | 					piov++; | 
 | 					fos += desc->len; | 
 | 				} | 
 | 				/* Returning NULLs for VIRTIO_BLK_T_GET_ID. */ | 
 | 				if (hdr.type & ~(VIRTIO_BLK_T_OUT | | 
 | 					VIRTIO_BLK_T_GET_ID)) { | 
 | 					/* | 
 | 					  VIRTIO_BLK_T_IN - does not do | 
 | 					  anything. Probably for documenting. | 
 | 					  VIRTIO_BLK_T_SCSI_CMD - for | 
 | 					  virtio_scsi. | 
 | 					  VIRTIO_BLK_T_FLUSH - turned off in | 
 | 					  config space. | 
 | 					  VIRTIO_BLK_T_BARRIER - defined but not | 
 | 					  used in anywhere. | 
 | 					*/ | 
 | 					mpsslog("%s() %d: type %x ", | 
 | 						__func__, __LINE__, | 
 | 						hdr.type); | 
 | 					mpsslog("is not supported\n"); | 
 | 					status = -ENOTSUP; | 
 |  | 
 | 				} else { | 
 | 					ret = transfer_blocks( | 
 | 					mic->mic_virtblk.virtio_block_fd, | 
 | 						iovec, | 
 | 						piov - iovec); | 
 | 					if (ret < 0 && | 
 | 					    status != 0) | 
 | 						status = ret; | 
 | 				} | 
 | 				/* write status and update used pointer */ | 
 | 				if (status != 0) | 
 | 					status = status_error_check(desc); | 
 | 				ret = write_status( | 
 | 					mic->mic_virtblk.virtio_block_fd, | 
 | 					&status); | 
 | #ifdef DEBUG | 
 | 				mpsslog("%s() %d: write status=%d on desc=%p\n", | 
 | 					__func__, __LINE__, | 
 | 					status, desc); | 
 | #endif | 
 | 			} | 
 | 		} | 
 | 		free(iovec); | 
 | _stop_virtblk: | 
 | 		stop_virtblk(mic); | 
 | _close_backend: | 
 | 		close_backend(mic); | 
 | 	}  /* forever */ | 
 |  | 
 | 	pthread_exit(NULL); | 
 | } | 
 |  | 
 | static void | 
 | reset(struct mic_info *mic) | 
 | { | 
 | #define RESET_TIMEOUT 120 | 
 | 	int i = RESET_TIMEOUT; | 
 | 	setsysfs(mic->name, "state", "reset"); | 
 | 	while (i) { | 
 | 		char *state; | 
 | 		state = readsysfs(mic->name, "state"); | 
 | 		if (!state) | 
 | 			goto retry; | 
 | 		mpsslog("%s: %s %d state %s\n", | 
 | 			mic->name, __func__, __LINE__, state); | 
 |  | 
 | 		if (!strcmp(state, "ready")) { | 
 | 			free(state); | 
 | 			break; | 
 | 		} | 
 | 		free(state); | 
 | retry: | 
 | 		sleep(1); | 
 | 		i--; | 
 | 	} | 
 | } | 
 |  | 
 | static int | 
 | get_mic_shutdown_status(struct mic_info *mic, char *shutdown_status) | 
 | { | 
 | 	if (!strcmp(shutdown_status, "nop")) | 
 | 		return MIC_NOP; | 
 | 	if (!strcmp(shutdown_status, "crashed")) | 
 | 		return MIC_CRASHED; | 
 | 	if (!strcmp(shutdown_status, "halted")) | 
 | 		return MIC_HALTED; | 
 | 	if (!strcmp(shutdown_status, "poweroff")) | 
 | 		return MIC_POWER_OFF; | 
 | 	if (!strcmp(shutdown_status, "restart")) | 
 | 		return MIC_RESTART; | 
 | 	mpsslog("%s: BUG invalid status %s\n", mic->name, shutdown_status); | 
 | 	/* Invalid state */ | 
 | 	assert(0); | 
 | }; | 
 |  | 
 | static int get_mic_state(struct mic_info *mic) | 
 | { | 
 | 	char *state = NULL; | 
 | 	enum mic_states mic_state; | 
 |  | 
 | 	while (!state) { | 
 | 		state = readsysfs(mic->name, "state"); | 
 | 		sleep(1); | 
 | 	} | 
 | 	mpsslog("%s: %s %d state %s\n", | 
 | 		mic->name, __func__, __LINE__, state); | 
 |  | 
 | 	if (!strcmp(state, "ready")) { | 
 | 		mic_state = MIC_READY; | 
 | 	} else if (!strcmp(state, "booting")) { | 
 | 		mic_state = MIC_BOOTING; | 
 | 	} else if (!strcmp(state, "online")) { | 
 | 		mic_state = MIC_ONLINE; | 
 | 	} else if (!strcmp(state, "shutting_down")) { | 
 | 		mic_state = MIC_SHUTTING_DOWN; | 
 | 	} else if (!strcmp(state, "reset_failed")) { | 
 | 		mic_state = MIC_RESET_FAILED; | 
 | 	} else if (!strcmp(state, "resetting")) { | 
 | 		mic_state = MIC_RESETTING; | 
 | 	} else { | 
 | 		mpsslog("%s: BUG invalid state %s\n", mic->name, state); | 
 | 		assert(0); | 
 | 	} | 
 |  | 
 | 	free(state); | 
 | 	return mic_state; | 
 | }; | 
 |  | 
 | static void mic_handle_shutdown(struct mic_info *mic) | 
 | { | 
 | #define SHUTDOWN_TIMEOUT 60 | 
 | 	int i = SHUTDOWN_TIMEOUT; | 
 | 	char *shutdown_status; | 
 | 	while (i) { | 
 | 		shutdown_status = readsysfs(mic->name, "shutdown_status"); | 
 | 		if (!shutdown_status) { | 
 | 			sleep(1); | 
 | 			continue; | 
 | 		} | 
 | 		mpsslog("%s: %s %d shutdown_status %s\n", | 
 | 			mic->name, __func__, __LINE__, shutdown_status); | 
 | 		switch (get_mic_shutdown_status(mic, shutdown_status)) { | 
 | 		case MIC_RESTART: | 
 | 			mic->restart = 1; | 
 | 		case MIC_HALTED: | 
 | 		case MIC_POWER_OFF: | 
 | 		case MIC_CRASHED: | 
 | 			free(shutdown_status); | 
 | 			goto reset; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 		free(shutdown_status); | 
 | 		sleep(1); | 
 | 		i--; | 
 | 	} | 
 | reset: | 
 | 	if (!i) | 
 | 		mpsslog("%s: %s %d timing out waiting for shutdown_status %s\n", | 
 | 			mic->name, __func__, __LINE__, shutdown_status); | 
 | 	reset(mic); | 
 | } | 
 |  | 
 | static int open_state_fd(struct mic_info *mic) | 
 | { | 
 | 	char pathname[PATH_MAX]; | 
 | 	int fd; | 
 |  | 
 | 	snprintf(pathname, PATH_MAX - 1, "%s/%s/%s", | 
 | 		 MICSYSFSDIR, mic->name, "state"); | 
 |  | 
 | 	fd = open(pathname, O_RDONLY); | 
 | 	if (fd < 0) | 
 | 		mpsslog("%s: opening file %s failed %s\n", | 
 | 			mic->name, pathname, strerror(errno)); | 
 | 	return fd; | 
 | } | 
 |  | 
 | static int block_till_state_change(int fd, struct mic_info *mic) | 
 | { | 
 | 	struct pollfd ufds[1]; | 
 | 	char value[PAGE_SIZE]; | 
 | 	int ret; | 
 |  | 
 | 	ufds[0].fd = fd; | 
 | 	ufds[0].events = POLLERR | POLLPRI; | 
 | 	ret = poll(ufds, 1, -1); | 
 | 	if (ret < 0) { | 
 | 		mpsslog("%s: %s %d poll failed %s\n", | 
 | 			mic->name, __func__, __LINE__, strerror(errno)); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	ret = lseek(fd, 0, SEEK_SET); | 
 | 	if (ret < 0) { | 
 | 		mpsslog("%s: %s %d Failed to seek to 0: %s\n", | 
 | 			mic->name, __func__, __LINE__, strerror(errno)); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	ret = read(fd, value, sizeof(value)); | 
 | 	if (ret < 0) { | 
 | 		mpsslog("%s: %s %d Failed to read sysfs entry: %s\n", | 
 | 			mic->name, __func__, __LINE__, strerror(errno)); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void * | 
 | mic_config(void *arg) | 
 | { | 
 | 	struct mic_info *mic = (struct mic_info *)arg; | 
 | 	int fd, ret, stat = 0; | 
 |  | 
 | 	fd = open_state_fd(mic); | 
 | 	if (fd < 0) { | 
 | 		mpsslog("%s: %s %d open state fd failed %s\n", | 
 | 			mic->name, __func__, __LINE__, strerror(errno)); | 
 | 		goto exit; | 
 | 	} | 
 |  | 
 | 	do { | 
 | 		ret = block_till_state_change(fd, mic); | 
 | 		if (ret < 0) { | 
 | 			mpsslog("%s: %s %d block_till_state_change error %s\n", | 
 | 				mic->name, __func__, __LINE__, strerror(errno)); | 
 | 			goto close_exit; | 
 | 		} | 
 |  | 
 | 		switch (get_mic_state(mic)) { | 
 | 		case MIC_SHUTTING_DOWN: | 
 | 			mic_handle_shutdown(mic); | 
 | 			break; | 
 | 		case MIC_READY: | 
 | 		case MIC_RESET_FAILED: | 
 | 			ret = kill(mic->pid, SIGTERM); | 
 | 			mpsslog("%s: %s %d kill pid %d ret %d\n", | 
 | 				mic->name, __func__, __LINE__, | 
 | 				mic->pid, ret); | 
 | 			if (!ret) { | 
 | 				ret = waitpid(mic->pid, &stat, | 
 | 					      WIFSIGNALED(stat)); | 
 | 				mpsslog("%s: %s %d waitpid ret %d pid %d\n", | 
 | 					mic->name, __func__, __LINE__, | 
 | 					ret, mic->pid); | 
 | 			} | 
 | 			if (mic->boot_on_resume) { | 
 | 				setsysfs(mic->name, "state", "boot"); | 
 | 				mic->boot_on_resume = 0; | 
 | 			} | 
 | 			goto close_exit; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 	} while (1); | 
 |  | 
 | close_exit: | 
 | 	close(fd); | 
 | exit: | 
 | 	init_mic(mic); | 
 | 	pthread_exit(NULL); | 
 | } | 
 |  | 
 | static void | 
 | set_cmdline(struct mic_info *mic) | 
 | { | 
 | 	char buffer[PATH_MAX]; | 
 | 	int len; | 
 |  | 
 | 	len = snprintf(buffer, PATH_MAX, | 
 | 		"clocksource=tsc highres=off nohz=off "); | 
 | 	len += snprintf(buffer + len, PATH_MAX - len, | 
 | 		"cpufreq_on;corec6_off;pc3_off;pc6_off "); | 
 | 	len += snprintf(buffer + len, PATH_MAX - len, | 
 | 		"ifcfg=static;address,172.31.%d.1;netmask,255.255.255.0", | 
 | 		mic->id + 1); | 
 |  | 
 | 	setsysfs(mic->name, "cmdline", buffer); | 
 | 	mpsslog("%s: Command line: \"%s\"\n", mic->name, buffer); | 
 | 	snprintf(buffer, PATH_MAX, "172.31.%d.1", mic->id + 1); | 
 | 	mpsslog("%s: IPADDR: \"%s\"\n", mic->name, buffer); | 
 | } | 
 |  | 
 | static void | 
 | set_log_buf_info(struct mic_info *mic) | 
 | { | 
 | 	int fd; | 
 | 	off_t len; | 
 | 	char system_map[] = "/lib/firmware/mic/System.map"; | 
 | 	char *map, *temp, log_buf[17] = {'\0'}; | 
 |  | 
 | 	fd = open(system_map, O_RDONLY); | 
 | 	if (fd < 0) { | 
 | 		mpsslog("%s: Opening System.map failed: %d\n", | 
 | 			mic->name, errno); | 
 | 		return; | 
 | 	} | 
 | 	len = lseek(fd, 0, SEEK_END); | 
 | 	if (len < 0) { | 
 | 		mpsslog("%s: Reading System.map size failed: %d\n", | 
 | 			mic->name, errno); | 
 | 		close(fd); | 
 | 		return; | 
 | 	} | 
 | 	map = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); | 
 | 	if (map == MAP_FAILED) { | 
 | 		mpsslog("%s: mmap of System.map failed: %d\n", | 
 | 			mic->name, errno); | 
 | 		close(fd); | 
 | 		return; | 
 | 	} | 
 | 	temp = strstr(map, "__log_buf"); | 
 | 	if (!temp) { | 
 | 		mpsslog("%s: __log_buf not found: %d\n", mic->name, errno); | 
 | 		munmap(map, len); | 
 | 		close(fd); | 
 | 		return; | 
 | 	} | 
 | 	strncpy(log_buf, temp - 19, 16); | 
 | 	setsysfs(mic->name, "log_buf_addr", log_buf); | 
 | 	mpsslog("%s: log_buf_addr: %s\n", mic->name, log_buf); | 
 | 	temp = strstr(map, "log_buf_len"); | 
 | 	if (!temp) { | 
 | 		mpsslog("%s: log_buf_len not found: %d\n", mic->name, errno); | 
 | 		munmap(map, len); | 
 | 		close(fd); | 
 | 		return; | 
 | 	} | 
 | 	strncpy(log_buf, temp - 19, 16); | 
 | 	setsysfs(mic->name, "log_buf_len", log_buf); | 
 | 	mpsslog("%s: log_buf_len: %s\n", mic->name, log_buf); | 
 | 	munmap(map, len); | 
 | 	close(fd); | 
 | } | 
 |  | 
 | static void | 
 | change_virtblk_backend(int x, siginfo_t *siginfo, void *p) | 
 | { | 
 | 	struct mic_info *mic; | 
 |  | 
 | 	for (mic = mic_list.next; mic != NULL; mic = mic->next) | 
 | 		mic->mic_virtblk.signaled = 1/* true */; | 
 | } | 
 |  | 
 | static void | 
 | set_mic_boot_params(struct mic_info *mic) | 
 | { | 
 | 	set_log_buf_info(mic); | 
 | 	set_cmdline(mic); | 
 | } | 
 |  | 
 | static void * | 
 | init_mic(void *arg) | 
 | { | 
 | 	struct mic_info *mic = (struct mic_info *)arg; | 
 | 	struct sigaction ignore = { | 
 | 		.sa_flags = 0, | 
 | 		.sa_handler = SIG_IGN | 
 | 	}; | 
 | 	struct sigaction act = { | 
 | 		.sa_flags = SA_SIGINFO, | 
 | 		.sa_sigaction = change_virtblk_backend, | 
 | 	}; | 
 | 	char buffer[PATH_MAX]; | 
 | 	int err, fd; | 
 |  | 
 | 	/* | 
 | 	 * Currently, one virtio block device is supported for each MIC card | 
 | 	 * at a time. Any user (or test) can send a SIGUSR1 to the MIC daemon. | 
 | 	 * The signal informs the virtio block backend about a change in the | 
 | 	 * configuration file which specifies the virtio backend file name on | 
 | 	 * the host. Virtio block backend then re-reads the configuration file | 
 | 	 * and switches to the new block device. This signalling mechanism may | 
 | 	 * not be required once multiple virtio block devices are supported by | 
 | 	 * the MIC daemon. | 
 | 	 */ | 
 | 	sigaction(SIGUSR1, &ignore, NULL); | 
 | retry: | 
 | 	fd = open_state_fd(mic); | 
 | 	if (fd < 0) { | 
 | 		mpsslog("%s: %s %d open state fd failed %s\n", | 
 | 			mic->name, __func__, __LINE__, strerror(errno)); | 
 | 		sleep(2); | 
 | 		goto retry; | 
 | 	} | 
 |  | 
 | 	if (mic->restart) { | 
 | 		snprintf(buffer, PATH_MAX, "boot"); | 
 | 		setsysfs(mic->name, "state", buffer); | 
 | 		mpsslog("%s restarting mic %d\n", | 
 | 			mic->name, mic->restart); | 
 | 		mic->restart = 0; | 
 | 	} | 
 |  | 
 | 	while (1) { | 
 | 		while (block_till_state_change(fd, mic)) { | 
 | 			mpsslog("%s: %s %d block_till_state_change error %s\n", | 
 | 				mic->name, __func__, __LINE__, strerror(errno)); | 
 | 			sleep(2); | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		if (get_mic_state(mic) == MIC_BOOTING) | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	mic->pid = fork(); | 
 | 	switch (mic->pid) { | 
 | 	case 0: | 
 | 		add_virtio_device(mic, &virtcons_dev_page.dd); | 
 | 		add_virtio_device(mic, &virtnet_dev_page.dd); | 
 | 		err = pthread_create(&mic->mic_console.console_thread, NULL, | 
 | 			virtio_console, mic); | 
 | 		if (err) | 
 | 			mpsslog("%s virtcons pthread_create failed %s\n", | 
 | 				mic->name, strerror(err)); | 
 | 		err = pthread_create(&mic->mic_net.net_thread, NULL, | 
 | 			virtio_net, mic); | 
 | 		if (err) | 
 | 			mpsslog("%s virtnet pthread_create failed %s\n", | 
 | 				mic->name, strerror(err)); | 
 | 		err = pthread_create(&mic->mic_virtblk.block_thread, NULL, | 
 | 			virtio_block, mic); | 
 | 		if (err) | 
 | 			mpsslog("%s virtblk pthread_create failed %s\n", | 
 | 				mic->name, strerror(err)); | 
 | 		sigemptyset(&act.sa_mask); | 
 | 		err = sigaction(SIGUSR1, &act, NULL); | 
 | 		if (err) | 
 | 			mpsslog("%s sigaction SIGUSR1 failed %s\n", | 
 | 				mic->name, strerror(errno)); | 
 | 		while (1) | 
 | 			sleep(60); | 
 | 	case -1: | 
 | 		mpsslog("fork failed MIC name %s id %d errno %d\n", | 
 | 			mic->name, mic->id, errno); | 
 | 		break; | 
 | 	default: | 
 | 		err = pthread_create(&mic->config_thread, NULL, | 
 | 				     mic_config, mic); | 
 | 		if (err) | 
 | 			mpsslog("%s mic_config pthread_create failed %s\n", | 
 | 				mic->name, strerror(err)); | 
 | 	} | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static void | 
 | start_daemon(void) | 
 | { | 
 | 	struct mic_info *mic; | 
 | 	int err; | 
 |  | 
 | 	for (mic = mic_list.next; mic; mic = mic->next) { | 
 | 		set_mic_boot_params(mic); | 
 | 		err = pthread_create(&mic->init_thread, NULL, init_mic, mic); | 
 | 		if (err) | 
 | 			mpsslog("%s init_mic pthread_create failed %s\n", | 
 | 				mic->name, strerror(err)); | 
 | 	} | 
 |  | 
 | 	while (1) | 
 | 		sleep(60); | 
 | } | 
 |  | 
 | static int | 
 | init_mic_list(void) | 
 | { | 
 | 	struct mic_info *mic = &mic_list; | 
 | 	struct dirent *file; | 
 | 	DIR *dp; | 
 | 	int cnt = 0; | 
 |  | 
 | 	dp = opendir(MICSYSFSDIR); | 
 | 	if (!dp) | 
 | 		return 0; | 
 |  | 
 | 	while ((file = readdir(dp)) != NULL) { | 
 | 		if (!strncmp(file->d_name, "mic", 3)) { | 
 | 			mic->next = calloc(1, sizeof(struct mic_info)); | 
 | 			if (mic->next) { | 
 | 				mic = mic->next; | 
 | 				mic->id = atoi(&file->d_name[3]); | 
 | 				mic->name = malloc(strlen(file->d_name) + 16); | 
 | 				if (mic->name) | 
 | 					strcpy(mic->name, file->d_name); | 
 | 				mpsslog("MIC name %s id %d\n", mic->name, | 
 | 					mic->id); | 
 | 				cnt++; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	closedir(dp); | 
 | 	return cnt; | 
 | } | 
 |  | 
 | void | 
 | mpsslog(char *format, ...) | 
 | { | 
 | 	va_list args; | 
 | 	char buffer[4096]; | 
 | 	char ts[52], *ts1; | 
 | 	time_t t; | 
 |  | 
 | 	if (logfp == NULL) | 
 | 		return; | 
 |  | 
 | 	va_start(args, format); | 
 | 	vsprintf(buffer, format, args); | 
 | 	va_end(args); | 
 |  | 
 | 	time(&t); | 
 | 	ts1 = ctime_r(&t, ts); | 
 | 	ts1[strlen(ts1) - 1] = '\0'; | 
 | 	fprintf(logfp, "%s: %s", ts1, buffer); | 
 |  | 
 | 	fflush(logfp); | 
 | } | 
 |  | 
 | int | 
 | main(int argc, char *argv[]) | 
 | { | 
 | 	int cnt; | 
 | 	pid_t pid; | 
 |  | 
 | 	myname = argv[0]; | 
 |  | 
 | 	logfp = fopen(LOGFILE_NAME, "a+"); | 
 | 	if (!logfp) { | 
 | 		fprintf(stderr, "cannot open logfile '%s'\n", LOGFILE_NAME); | 
 | 		exit(1); | 
 | 	} | 
 | 	pid = fork(); | 
 | 	switch (pid) { | 
 | 	case 0: | 
 | 		break; | 
 | 	case -1: | 
 | 		exit(2); | 
 | 	default: | 
 | 		exit(0); | 
 | 	} | 
 |  | 
 | 	mpsslog("MIC Daemon start\n"); | 
 |  | 
 | 	cnt = init_mic_list(); | 
 | 	if (cnt == 0) { | 
 | 		mpsslog("MIC module not loaded\n"); | 
 | 		exit(3); | 
 | 	} | 
 | 	mpsslog("MIC found %d devices\n", cnt); | 
 |  | 
 | 	start_daemon(); | 
 |  | 
 | 	exit(0); | 
 | } |