Last active
October 31, 2019 11:52
-
-
Save gnif/77e7fb54604b42a1a98ecb8bf3d2cf46 to your computer and use it in GitHub Desktop.
Example client for the new zero-copy shared ram device @ https://github.com/gnif/qemu/blob/master/hw/misc/porthole.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <sys/socket.h> | |
#include <sys/un.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <stdint.h> | |
#include <sys/mman.h> | |
#include <sys/stat.h> | |
typedef struct { | |
uint32_t id; // the ID of the FD | |
} __attribute__ ((packed)) PHMsgFd; | |
typedef struct { | |
uint32_t fd_id; // the ID of the FD for this segment | |
uint32_t size; // the size of this segment | |
uint64_t addr; // the base address of this segment | |
} __attribute__ ((packed)) PHMsgSegment; | |
typedef struct { | |
uint32_t type; // the application defined type | |
uint32_t id; // the ID of the new mapping | |
} __attribute__ ((packed)) PHMsgFinish; | |
typedef struct { | |
uint32_t id; // the mapping ID | |
} __attribute__ ((packed)) PHMsgUnmap; | |
typedef struct { | |
uint32_t msg; | |
union | |
{ | |
PHMsgFd fd; | |
PHMsgSegment segment; | |
PHMsgFinish finish; | |
PHMsgUnmap unmap; | |
} u; | |
} __attribute__ ((packed)) PHMsg; | |
#define PH_MSG_MAP 0x1 // start of a map sequence | |
#define PH_MSG_FD 0x2 // file descriptor | |
#define PH_MSG_SEGMENT 0x3 // map segment | |
#define PH_MSG_FINISH 0x4 // finish of map sequence | |
#define PH_MSG_UNMAP 0x5 // unmap a previous map | |
#define PH_MSG_MAP_SIZE (sizeof(uint32_t)) | |
#define PH_MSG_FD_SIZE (sizeof(uint32_t) + sizeof(PHMsgFd)) | |
#define PH_MSG_SEGMENT_SIZE (sizeof(uint32_t) + sizeof(PHMsgSegment)) | |
#define PH_MSG_FINISH_SIZE (sizeof(uint32_t) + sizeof(PHMsgFinish)) | |
#define PH_MSG_UNMAP_SIZE (sizeof(uint32_t) + sizeof(PHMsgUnmap)) | |
struct memorymap | |
{ | |
uint32_t fd_id; | |
int fd; | |
char * map; | |
size_t size; | |
}; | |
struct memorymap pool[4] = {0}; | |
void DumpHex(const void* data, size_t size) { | |
char ascii[17]; | |
size_t i, j; | |
ascii[16] = '\0'; | |
for (i = 0; i < size; ++i) { | |
printf("%02X ", ((unsigned char*)data)[i]); | |
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') { | |
ascii[i % 16] = ((unsigned char*)data)[i]; | |
} else { | |
ascii[i % 16] = '.'; | |
} | |
if ((i+1) % 8 == 0 || i+1 == size) { | |
printf(" "); | |
if ((i+1) % 16 == 0) { | |
printf("| %s \n", ascii); | |
} else if (i+1 == size) { | |
ascii[(i+1) % 16] = '\0'; | |
if ((i+1) % 16 <= 8) { | |
printf(" "); | |
} | |
for (j = (i+1) % 16; j < 16; ++j) { | |
printf(" "); | |
} | |
printf("| %s \n", ascii); | |
} | |
} | |
} | |
} | |
int process(int fd) | |
{ | |
PHMsg msg; | |
struct iovec io = | |
{ | |
.iov_base = &msg, | |
.iov_len = sizeof(msg) | |
}; | |
char buffer[256] = {0}; | |
struct msghdr msghdr = | |
{ | |
.msg_iov = &io, | |
.msg_iovlen = 1, | |
.msg_control = &buffer, | |
.msg_controllen = sizeof(buffer) | |
}; | |
if (recvmsg(fd, &msghdr, 0) < 0) | |
{ | |
fprintf(stderr, "Failed to recieve the message\n"); | |
return -1; | |
} | |
switch(msg.msg) | |
{ | |
case PH_MSG_MAP: | |
printf("Map Message\n"); | |
break; | |
case PH_MSG_FD: | |
{ | |
/* get the fd */ | |
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msghdr); | |
unsigned char * data = CMSG_DATA(cmsg); | |
int memfd = *((int*)data); | |
/* see if we already have this id */ | |
int i; | |
for(i = 0; i < 4; ++i) | |
if (pool[i].fd_id == msg.u.fd.id && pool[i].map) | |
break; | |
/* if not found */ | |
if (i == 4) | |
{ | |
/* look for a free place in the pool table */ | |
for(i = 0; i < 4; ++i) | |
{ | |
if (pool[i].map) | |
continue; | |
struct stat st; | |
fstat(memfd, &st); | |
pool[i].fd_id = msg.u.fd.id; | |
pool[i].fd = memfd; | |
pool[i].map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, memfd, 0); | |
pool[i].size = st.st_size; | |
if (!pool[i].map) | |
{ | |
fprintf(stderr, "Failed to mmap the supplied fd\n"); | |
close(memfd); | |
return -1; | |
} | |
printf("mapped fd_id 0x%08x to %p (%lu)\n", | |
msg.u.fd.id, pool[i].map, st.st_size); | |
break; | |
} | |
if (i == 4) | |
{ | |
fprintf(stderr, "mempool full\n"); | |
close(memfd); | |
return -1; | |
} | |
} | |
break; | |
} | |
case PH_MSG_SEGMENT: | |
{ | |
int i; | |
for(i = 0; i < 4; ++i) | |
if (pool[i].map && pool[i].fd_id == msg.u.segment.fd_id) | |
break; | |
if (i == 4) | |
{ | |
fprintf(stderr, "No such fd_id: 0x%08x\n", msg.u.segment.fd_id); | |
return -1; | |
} | |
char * map = pool[i].map; | |
printf("FD: 0x%08x, addr: 0x%09lx, size: %6u - ", msg.u.segment.fd_id, msg.u.segment.addr, msg.u.segment.size); | |
DumpHex(map + msg.u.segment.addr, msg.u.segment.size > 16 ? 16 : msg.u.segment.size); | |
break; | |
} | |
case PH_MSG_FINISH: | |
printf("Type: %u, ID: %u\n", msg.u.finish.type, msg.u.finish.id); | |
break; | |
case PH_MSG_UNMAP: | |
printf("Unmap: %u\n", msg.u.unmap.id); | |
uint32_t reply = PH_MSG_UNMAP; | |
msghdr.msg_controllen = 0; | |
io.iov_base = &reply; | |
io.iov_len = sizeof(reply); | |
usleep(10000000); | |
printf("Sending reply\n"); | |
if (sendmsg(fd, &msghdr, 0) < 0) | |
{ | |
fprintf(stderr, "Failed to send the message\n"); | |
return -1; | |
} | |
break; | |
} | |
return 0; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
if (argc < 2) | |
{ | |
fprintf(stderr, "No socket specified\n"); | |
return -1; | |
} | |
int fd = socket(AF_UNIX, SOCK_STREAM, 0); | |
if (fd == -1) | |
{ | |
fprintf(stderr, "Failed to create unix socket\n"); | |
return -1; | |
} | |
struct sockaddr_un addr; | |
memset(&addr, 0, sizeof(addr)); | |
addr.sun_family = AF_UNIX; | |
strncpy(addr.sun_path, argv[1], sizeof(addr.sun_path)-1); | |
if (connect(fd, (const struct sockaddr*)&addr, sizeof(addr)) == -1) | |
{ | |
fprintf(stderr, "Failed to connect to the socket\n"); | |
close(fd); | |
return -1; | |
} | |
int ret = 0; | |
while(ret == 0) | |
ret = process(fd); | |
/* cleanup */ | |
for(int i = 0; i < 4; ++i) | |
if (pool[i].map) | |
{ | |
munmap(pool[i].map, pool[i].size); | |
close(pool[i].fd); | |
} | |
close(fd); | |
return ret; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example output: