PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/contrib/ivshmem-server/ivshmem-server.c

https://github.com/mstsirkin/qemu
C | 457 lines | 338 code | 72 blank | 47 comment | 54 complexity | 6b7f0a28453bacada7a5d6f820c925ee MD5 | raw file
  1. /*
  2. * Copyright 6WIND S.A., 2014
  3. *
  4. * This work is licensed under the terms of the GNU GPL, version 2 or
  5. * (at your option) any later version. See the COPYING file in the
  6. * top-level directory.
  7. */
  8. #include "qemu/osdep.h"
  9. #include "qemu-common.h"
  10. #include "qemu/host-utils.h"
  11. #include "qemu/sockets.h"
  12. #include <sys/socket.h>
  13. #include <sys/un.h>
  14. #include "ivshmem-server.h"
  15. /* log a message on stdout if verbose=1 */
  16. #define IVSHMEM_SERVER_DEBUG(server, fmt, ...) do { \
  17. if ((server)->verbose) { \
  18. printf(fmt, ## __VA_ARGS__); \
  19. } \
  20. } while (0)
  21. /** maximum size of a huge page, used by ivshmem_server_ftruncate() */
  22. #define IVSHMEM_SERVER_MAX_HUGEPAGE_SIZE (1024 * 1024 * 1024)
  23. /** default listen backlog (number of sockets not accepted) */
  24. #define IVSHMEM_SERVER_LISTEN_BACKLOG 10
  25. /* send message to a client unix socket */
  26. static int
  27. ivshmem_server_send_one_msg(int sock_fd, int64_t peer_id, int fd)
  28. {
  29. int ret;
  30. struct msghdr msg;
  31. struct iovec iov[1];
  32. union {
  33. struct cmsghdr cmsg;
  34. char control[CMSG_SPACE(sizeof(int))];
  35. } msg_control;
  36. struct cmsghdr *cmsg;
  37. peer_id = GINT64_TO_LE(peer_id);
  38. iov[0].iov_base = &peer_id;
  39. iov[0].iov_len = sizeof(peer_id);
  40. memset(&msg, 0, sizeof(msg));
  41. msg.msg_iov = iov;
  42. msg.msg_iovlen = 1;
  43. /* if fd is specified, add it in a cmsg */
  44. if (fd >= 0) {
  45. memset(&msg_control, 0, sizeof(msg_control));
  46. msg.msg_control = &msg_control;
  47. msg.msg_controllen = sizeof(msg_control);
  48. cmsg = CMSG_FIRSTHDR(&msg);
  49. cmsg->cmsg_level = SOL_SOCKET;
  50. cmsg->cmsg_type = SCM_RIGHTS;
  51. cmsg->cmsg_len = CMSG_LEN(sizeof(int));
  52. memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
  53. }
  54. ret = sendmsg(sock_fd, &msg, 0);
  55. if (ret <= 0) {
  56. return -1;
  57. }
  58. return 0;
  59. }
  60. /* free a peer when the server advertises a disconnection or when the
  61. * server is freed */
  62. static void
  63. ivshmem_server_free_peer(IvshmemServer *server, IvshmemServerPeer *peer)
  64. {
  65. unsigned vector;
  66. IvshmemServerPeer *other_peer;
  67. IVSHMEM_SERVER_DEBUG(server, "free peer %" PRId64 "\n", peer->id);
  68. close(peer->sock_fd);
  69. QTAILQ_REMOVE(&server->peer_list, peer, next);
  70. /* advertise the deletion to other peers */
  71. QTAILQ_FOREACH(other_peer, &server->peer_list, next) {
  72. ivshmem_server_send_one_msg(other_peer->sock_fd, peer->id, -1);
  73. }
  74. for (vector = 0; vector < peer->vectors_count; vector++) {
  75. event_notifier_cleanup(&peer->vectors[vector]);
  76. }
  77. g_free(peer);
  78. }
  79. /* send the peer id and the shm_fd just after a new client connection */
  80. static int
  81. ivshmem_server_send_initial_info(IvshmemServer *server, IvshmemServerPeer *peer)
  82. {
  83. int ret;
  84. /* send our protocol version first */
  85. ret = ivshmem_server_send_one_msg(peer->sock_fd, IVSHMEM_PROTOCOL_VERSION,
  86. -1);
  87. if (ret < 0) {
  88. IVSHMEM_SERVER_DEBUG(server, "cannot send version: %s\n",
  89. strerror(errno));
  90. return -1;
  91. }
  92. /* send the peer id to the client */
  93. ret = ivshmem_server_send_one_msg(peer->sock_fd, peer->id, -1);
  94. if (ret < 0) {
  95. IVSHMEM_SERVER_DEBUG(server, "cannot send peer id: %s\n",
  96. strerror(errno));
  97. return -1;
  98. }
  99. /* send the shm_fd */
  100. ret = ivshmem_server_send_one_msg(peer->sock_fd, -1, server->shm_fd);
  101. if (ret < 0) {
  102. IVSHMEM_SERVER_DEBUG(server, "cannot send shm fd: %s\n",
  103. strerror(errno));
  104. return -1;
  105. }
  106. return 0;
  107. }
  108. /* handle message on listening unix socket (new client connection) */
  109. static int
  110. ivshmem_server_handle_new_conn(IvshmemServer *server)
  111. {
  112. IvshmemServerPeer *peer, *other_peer;
  113. struct sockaddr_un unaddr;
  114. socklen_t unaddr_len;
  115. int newfd;
  116. unsigned i;
  117. /* accept the incoming connection */
  118. unaddr_len = sizeof(unaddr);
  119. newfd = qemu_accept(server->sock_fd,
  120. (struct sockaddr *)&unaddr, &unaddr_len);
  121. if (newfd < 0) {
  122. IVSHMEM_SERVER_DEBUG(server, "cannot accept() %s\n", strerror(errno));
  123. return -1;
  124. }
  125. qemu_set_nonblock(newfd);
  126. IVSHMEM_SERVER_DEBUG(server, "accept()=%d\n", newfd);
  127. /* allocate new structure for this peer */
  128. peer = g_malloc0(sizeof(*peer));
  129. peer->sock_fd = newfd;
  130. /* get an unused peer id */
  131. /* XXX: this could use id allocation such as Linux IDA, or simply
  132. * a free-list */
  133. for (i = 0; i < G_MAXUINT16; i++) {
  134. if (ivshmem_server_search_peer(server, server->cur_id) == NULL) {
  135. break;
  136. }
  137. server->cur_id++;
  138. }
  139. if (i == G_MAXUINT16) {
  140. IVSHMEM_SERVER_DEBUG(server, "cannot allocate new client id\n");
  141. close(newfd);
  142. g_free(peer);
  143. return -1;
  144. }
  145. peer->id = server->cur_id++;
  146. /* create eventfd, one per vector */
  147. peer->vectors_count = server->n_vectors;
  148. for (i = 0; i < peer->vectors_count; i++) {
  149. if (event_notifier_init(&peer->vectors[i], FALSE) < 0) {
  150. IVSHMEM_SERVER_DEBUG(server, "cannot create eventfd\n");
  151. goto fail;
  152. }
  153. }
  154. /* send peer id and shm fd */
  155. if (ivshmem_server_send_initial_info(server, peer) < 0) {
  156. IVSHMEM_SERVER_DEBUG(server, "cannot send initial info\n");
  157. goto fail;
  158. }
  159. /* advertise the new peer to others */
  160. QTAILQ_FOREACH(other_peer, &server->peer_list, next) {
  161. for (i = 0; i < peer->vectors_count; i++) {
  162. ivshmem_server_send_one_msg(other_peer->sock_fd, peer->id,
  163. peer->vectors[i].wfd);
  164. }
  165. }
  166. /* advertise the other peers to the new one */
  167. QTAILQ_FOREACH(other_peer, &server->peer_list, next) {
  168. for (i = 0; i < peer->vectors_count; i++) {
  169. ivshmem_server_send_one_msg(peer->sock_fd, other_peer->id,
  170. other_peer->vectors[i].wfd);
  171. }
  172. }
  173. /* advertise the new peer to itself */
  174. for (i = 0; i < peer->vectors_count; i++) {
  175. ivshmem_server_send_one_msg(peer->sock_fd, peer->id,
  176. event_notifier_get_fd(&peer->vectors[i]));
  177. }
  178. QTAILQ_INSERT_TAIL(&server->peer_list, peer, next);
  179. IVSHMEM_SERVER_DEBUG(server, "new peer id = %" PRId64 "\n",
  180. peer->id);
  181. return 0;
  182. fail:
  183. while (i--) {
  184. event_notifier_cleanup(&peer->vectors[i]);
  185. }
  186. close(newfd);
  187. g_free(peer);
  188. return -1;
  189. }
  190. /* Try to ftruncate a file to next power of 2 of shmsize.
  191. * If it fails; all power of 2 above shmsize are tested until
  192. * we reach the maximum huge page size. This is useful
  193. * if the shm file is in a hugetlbfs that cannot be truncated to the
  194. * shm_size value. */
  195. static int
  196. ivshmem_server_ftruncate(int fd, unsigned shmsize)
  197. {
  198. int ret;
  199. struct stat mapstat;
  200. /* align shmsize to next power of 2 */
  201. shmsize = pow2ceil(shmsize);
  202. if (fstat(fd, &mapstat) != -1 && mapstat.st_size == shmsize) {
  203. return 0;
  204. }
  205. while (shmsize <= IVSHMEM_SERVER_MAX_HUGEPAGE_SIZE) {
  206. ret = ftruncate(fd, shmsize);
  207. if (ret == 0) {
  208. return ret;
  209. }
  210. shmsize *= 2;
  211. }
  212. return -1;
  213. }
  214. /* Init a new ivshmem server */
  215. int
  216. ivshmem_server_init(IvshmemServer *server, const char *unix_sock_path,
  217. const char *shm_path, bool use_shm_open,
  218. size_t shm_size, unsigned n_vectors,
  219. bool verbose)
  220. {
  221. int ret;
  222. memset(server, 0, sizeof(*server));
  223. server->verbose = verbose;
  224. ret = snprintf(server->unix_sock_path, sizeof(server->unix_sock_path),
  225. "%s", unix_sock_path);
  226. if (ret < 0 || ret >= sizeof(server->unix_sock_path)) {
  227. IVSHMEM_SERVER_DEBUG(server, "could not copy unix socket path\n");
  228. return -1;
  229. }
  230. ret = snprintf(server->shm_path, sizeof(server->shm_path),
  231. "%s", shm_path);
  232. if (ret < 0 || ret >= sizeof(server->shm_path)) {
  233. IVSHMEM_SERVER_DEBUG(server, "could not copy shm path\n");
  234. return -1;
  235. }
  236. server->use_shm_open = use_shm_open;
  237. server->shm_size = shm_size;
  238. server->n_vectors = n_vectors;
  239. QTAILQ_INIT(&server->peer_list);
  240. return 0;
  241. }
  242. /* open shm, create and bind to the unix socket */
  243. int
  244. ivshmem_server_start(IvshmemServer *server)
  245. {
  246. struct sockaddr_un sun;
  247. int shm_fd, sock_fd, ret;
  248. /* open shm file */
  249. if (server->use_shm_open) {
  250. IVSHMEM_SERVER_DEBUG(server, "Using POSIX shared memory: %s\n",
  251. server->shm_path);
  252. shm_fd = shm_open(server->shm_path, O_CREAT | O_RDWR, S_IRWXU);
  253. } else {
  254. gchar *filename = g_strdup_printf("%s/ivshmem.XXXXXX", server->shm_path);
  255. IVSHMEM_SERVER_DEBUG(server, "Using file-backed shared memory: %s\n",
  256. server->shm_path);
  257. shm_fd = mkstemp(filename);
  258. unlink(filename);
  259. g_free(filename);
  260. }
  261. if (shm_fd < 0) {
  262. fprintf(stderr, "cannot open shm file %s: %s\n", server->shm_path,
  263. strerror(errno));
  264. return -1;
  265. }
  266. if (ivshmem_server_ftruncate(shm_fd, server->shm_size) < 0) {
  267. fprintf(stderr, "ftruncate(%s) failed: %s\n", server->shm_path,
  268. strerror(errno));
  269. goto err_close_shm;
  270. }
  271. IVSHMEM_SERVER_DEBUG(server, "create & bind socket %s\n",
  272. server->unix_sock_path);
  273. /* create the unix listening socket */
  274. sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
  275. if (sock_fd < 0) {
  276. IVSHMEM_SERVER_DEBUG(server, "cannot create socket: %s\n",
  277. strerror(errno));
  278. goto err_close_shm;
  279. }
  280. sun.sun_family = AF_UNIX;
  281. ret = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s",
  282. server->unix_sock_path);
  283. if (ret < 0 || ret >= sizeof(sun.sun_path)) {
  284. IVSHMEM_SERVER_DEBUG(server, "could not copy unix socket path\n");
  285. goto err_close_sock;
  286. }
  287. if (bind(sock_fd, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
  288. IVSHMEM_SERVER_DEBUG(server, "cannot connect to %s: %s\n", sun.sun_path,
  289. strerror(errno));
  290. goto err_close_sock;
  291. }
  292. if (listen(sock_fd, IVSHMEM_SERVER_LISTEN_BACKLOG) < 0) {
  293. IVSHMEM_SERVER_DEBUG(server, "listen() failed: %s\n", strerror(errno));
  294. goto err_close_sock;
  295. }
  296. server->sock_fd = sock_fd;
  297. server->shm_fd = shm_fd;
  298. return 0;
  299. err_close_sock:
  300. close(sock_fd);
  301. err_close_shm:
  302. close(shm_fd);
  303. return -1;
  304. }
  305. /* close connections to clients, the unix socket and the shm fd */
  306. void
  307. ivshmem_server_close(IvshmemServer *server)
  308. {
  309. IvshmemServerPeer *peer, *npeer;
  310. IVSHMEM_SERVER_DEBUG(server, "close server\n");
  311. QTAILQ_FOREACH_SAFE(peer, &server->peer_list, next, npeer) {
  312. ivshmem_server_free_peer(server, peer);
  313. }
  314. unlink(server->unix_sock_path);
  315. close(server->sock_fd);
  316. close(server->shm_fd);
  317. server->sock_fd = -1;
  318. server->shm_fd = -1;
  319. }
  320. /* get the fd_set according to the unix socket and the peer list */
  321. void
  322. ivshmem_server_get_fds(const IvshmemServer *server, fd_set *fds, int *maxfd)
  323. {
  324. IvshmemServerPeer *peer;
  325. if (server->sock_fd == -1) {
  326. return;
  327. }
  328. FD_SET(server->sock_fd, fds);
  329. if (server->sock_fd >= *maxfd) {
  330. *maxfd = server->sock_fd + 1;
  331. }
  332. QTAILQ_FOREACH(peer, &server->peer_list, next) {
  333. FD_SET(peer->sock_fd, fds);
  334. if (peer->sock_fd >= *maxfd) {
  335. *maxfd = peer->sock_fd + 1;
  336. }
  337. }
  338. }
  339. /* process incoming messages on the sockets in fd_set */
  340. int
  341. ivshmem_server_handle_fds(IvshmemServer *server, fd_set *fds, int maxfd)
  342. {
  343. IvshmemServerPeer *peer, *peer_next;
  344. if (server->sock_fd < maxfd && FD_ISSET(server->sock_fd, fds) &&
  345. ivshmem_server_handle_new_conn(server) < 0 && errno != EINTR) {
  346. IVSHMEM_SERVER_DEBUG(server, "ivshmem_server_handle_new_conn() "
  347. "failed\n");
  348. return -1;
  349. }
  350. QTAILQ_FOREACH_SAFE(peer, &server->peer_list, next, peer_next) {
  351. /* any message from a peer socket result in a close() */
  352. IVSHMEM_SERVER_DEBUG(server, "peer->sock_fd=%d\n", peer->sock_fd);
  353. if (peer->sock_fd < maxfd && FD_ISSET(peer->sock_fd, fds)) {
  354. ivshmem_server_free_peer(server, peer);
  355. }
  356. }
  357. return 0;
  358. }
  359. /* lookup peer from its id */
  360. IvshmemServerPeer *
  361. ivshmem_server_search_peer(IvshmemServer *server, int64_t peer_id)
  362. {
  363. IvshmemServerPeer *peer;
  364. QTAILQ_FOREACH(peer, &server->peer_list, next) {
  365. if (peer->id == peer_id) {
  366. return peer;
  367. }
  368. }
  369. return NULL;
  370. }
  371. /* dump our info, the list of peers their vectors on stdout */
  372. void
  373. ivshmem_server_dump(const IvshmemServer *server)
  374. {
  375. const IvshmemServerPeer *peer;
  376. unsigned vector;
  377. /* dump peers */
  378. QTAILQ_FOREACH(peer, &server->peer_list, next) {
  379. printf("peer_id = %" PRId64 "\n", peer->id);
  380. for (vector = 0; vector < peer->vectors_count; vector++) {
  381. printf(" vector %d is enabled (fd=%d)\n", vector,
  382. event_notifier_get_fd(&peer->vectors[vector]));
  383. }
  384. }
  385. }