1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2016 Intel Corporation 3 */ 4 5 #include <sys/socket.h> 6 #include <sys/types.h> 7 #include <sys/stat.h> 8 #include <unistd.h> 9 #include <fcntl.h> 10 #include <sys/un.h> 11 #include <string.h> 12 #include <errno.h> 13 14 #include "vhost.h" 15 #include "virtio_user_dev.h" 16 17 /* The version of the protocol we support */ 18 #define VHOST_USER_VERSION 0x1 19 20 #define VHOST_MEMORY_MAX_NREGIONS 8 21 struct vhost_memory { 22 uint32_t nregions; 23 uint32_t padding; 24 struct vhost_memory_region regions[VHOST_MEMORY_MAX_NREGIONS]; 25 }; 26 27 struct vhost_user_msg { 28 enum vhost_user_request request; 29 30 #define VHOST_USER_VERSION_MASK 0x3 31 #define VHOST_USER_REPLY_MASK (0x1 << 2) 32 uint32_t flags; 33 uint32_t size; /* the following payload size */ 34 union { 35 #define VHOST_USER_VRING_IDX_MASK 0xff 36 #define VHOST_USER_VRING_NOFD_MASK (0x1 << 8) 37 uint64_t u64; 38 struct vhost_vring_state state; 39 struct vhost_vring_addr addr; 40 struct vhost_memory memory; 41 } payload; 42 int fds[VHOST_MEMORY_MAX_NREGIONS]; 43 } __attribute((packed)); 44 45 #define VHOST_USER_HDR_SIZE offsetof(struct vhost_user_msg, payload.u64) 46 #define VHOST_USER_PAYLOAD_SIZE \ 47 (sizeof(struct vhost_user_msg) - VHOST_USER_HDR_SIZE) 48 49 static int 50 vhost_user_write(int fd, void *buf, int len, int *fds, int fd_num) 51 { 52 int r; 53 struct msghdr msgh; 54 struct iovec iov; 55 size_t fd_size = fd_num * sizeof(int); 56 char control[CMSG_SPACE(fd_size)]; 57 struct cmsghdr *cmsg; 58 59 memset(&msgh, 0, sizeof(msgh)); 60 memset(control, 0, sizeof(control)); 61 62 iov.iov_base = (uint8_t *)buf; 63 iov.iov_len = len; 64 65 msgh.msg_iov = &iov; 66 msgh.msg_iovlen = 1; 67 msgh.msg_control = control; 68 msgh.msg_controllen = sizeof(control); 69 70 cmsg = CMSG_FIRSTHDR(&msgh); 71 cmsg->cmsg_len = CMSG_LEN(fd_size); 72 cmsg->cmsg_level = SOL_SOCKET; 73 cmsg->cmsg_type = SCM_RIGHTS; 74 memcpy(CMSG_DATA(cmsg), fds, fd_size); 75 76 do { 77 r = sendmsg(fd, &msgh, 0); 78 } while (r < 0 && errno == EINTR); 79 80 return r; 81 } 82 83 static int 84 vhost_user_read(int fd, struct vhost_user_msg *msg) 85 { 86 uint32_t valid_flags = VHOST_USER_REPLY_MASK | VHOST_USER_VERSION; 87 int ret, sz_hdr = VHOST_USER_HDR_SIZE, sz_payload; 88 89 ret = recv(fd, (void *)msg, sz_hdr, 0); 90 if (ret < sz_hdr) { 91 PMD_DRV_LOG(ERR, "Failed to recv msg hdr: %d instead of %d.", 92 ret, sz_hdr); 93 goto fail; 94 } 95 96 /* validate msg flags */ 97 if (msg->flags != (valid_flags)) { 98 PMD_DRV_LOG(ERR, "Failed to recv msg: flags %x instead of %x.", 99 msg->flags, valid_flags); 100 goto fail; 101 } 102 103 sz_payload = msg->size; 104 105 if ((size_t)sz_payload > sizeof(msg->payload)) 106 goto fail; 107 108 if (sz_payload) { 109 ret = recv(fd, (void *)((char *)msg + sz_hdr), sz_payload, 0); 110 if (ret < sz_payload) { 111 PMD_DRV_LOG(ERR, 112 "Failed to recv msg payload: %d instead of %d.", 113 ret, msg->size); 114 goto fail; 115 } 116 } 117 118 return 0; 119 120 fail: 121 return -1; 122 } 123 124 struct hugepage_file_info { 125 uint64_t addr; /**< virtual addr */ 126 size_t size; /**< the file size */ 127 char path[PATH_MAX]; /**< path to backing file */ 128 }; 129 130 /* Two possible options: 131 * 1. Match HUGEPAGE_INFO_FMT to find the file storing struct hugepage_file 132 * array. This is simple but cannot be used in secondary process because 133 * secondary process will close and munmap that file. 134 * 2. Match HUGEFILE_FMT to find hugepage files directly. 135 * 136 * We choose option 2. 137 */ 138 static int 139 get_hugepage_file_info(struct hugepage_file_info huges[], int max) 140 { 141 int idx; 142 FILE *f; 143 char buf[BUFSIZ], *tmp, *tail; 144 char *str_underline, *str_start; 145 int huge_index; 146 uint64_t v_start, v_end; 147 148 f = fopen("/proc/self/maps", "r"); 149 if (!f) { 150 PMD_DRV_LOG(ERR, "cannot open /proc/self/maps"); 151 return -1; 152 } 153 154 idx = 0; 155 while (fgets(buf, sizeof(buf), f) != NULL) { 156 if (sscanf(buf, "%" PRIx64 "-%" PRIx64, &v_start, &v_end) < 2) { 157 PMD_DRV_LOG(ERR, "Failed to parse address"); 158 goto error; 159 } 160 161 tmp = strchr(buf, ' ') + 1; /** skip address */ 162 tmp = strchr(tmp, ' ') + 1; /** skip perm */ 163 tmp = strchr(tmp, ' ') + 1; /** skip offset */ 164 tmp = strchr(tmp, ' ') + 1; /** skip dev */ 165 tmp = strchr(tmp, ' ') + 1; /** skip inode */ 166 while (*tmp == ' ') /** skip spaces */ 167 tmp++; 168 tail = strrchr(tmp, '\n'); /** remove newline if exists */ 169 if (tail) 170 *tail = '\0'; 171 172 /* Match HUGEFILE_FMT, aka "%s/%smap_%d", 173 * which is defined in eal_filesystem.h 174 */ 175 str_underline = strrchr(tmp, '_'); 176 if (!str_underline) 177 continue; 178 179 str_start = str_underline - strlen("map"); 180 if (str_start < tmp) 181 continue; 182 183 if (sscanf(str_start, "map_%d", &huge_index) != 1) 184 continue; 185 186 if (idx >= max) { 187 PMD_DRV_LOG(ERR, "Exceed maximum of %d", max); 188 goto error; 189 } 190 huges[idx].addr = v_start; 191 huges[idx].size = v_end - v_start; 192 snprintf(huges[idx].path, PATH_MAX, "%s", tmp); 193 idx++; 194 } 195 196 fclose(f); 197 return idx; 198 199 error: 200 fclose(f); 201 return -1; 202 } 203 204 static int 205 prepare_vhost_memory_user(struct vhost_user_msg *msg, int fds[]) 206 { 207 int i, num; 208 struct hugepage_file_info huges[VHOST_MEMORY_MAX_NREGIONS]; 209 struct vhost_memory_region *mr; 210 211 num = get_hugepage_file_info(huges, VHOST_MEMORY_MAX_NREGIONS); 212 if (num < 0) { 213 PMD_INIT_LOG(ERR, "Failed to prepare memory for vhost-user"); 214 return -1; 215 } 216 217 for (i = 0; i < num; ++i) { 218 mr = &msg->payload.memory.regions[i]; 219 mr->guest_phys_addr = huges[i].addr; /* use vaddr! */ 220 mr->userspace_addr = huges[i].addr; 221 mr->memory_size = huges[i].size; 222 mr->mmap_offset = 0; 223 fds[i] = open(huges[i].path, O_RDWR); 224 } 225 226 msg->payload.memory.nregions = num; 227 msg->payload.memory.padding = 0; 228 229 return 0; 230 } 231 232 static struct vhost_user_msg m; 233 234 const char * const vhost_msg_strings[] = { 235 [VHOST_USER_SET_OWNER] = "VHOST_SET_OWNER", 236 [VHOST_USER_RESET_OWNER] = "VHOST_RESET_OWNER", 237 [VHOST_USER_SET_FEATURES] = "VHOST_SET_FEATURES", 238 [VHOST_USER_GET_FEATURES] = "VHOST_GET_FEATURES", 239 [VHOST_USER_SET_VRING_CALL] = "VHOST_SET_VRING_CALL", 240 [VHOST_USER_SET_VRING_NUM] = "VHOST_SET_VRING_NUM", 241 [VHOST_USER_SET_VRING_BASE] = "VHOST_SET_VRING_BASE", 242 [VHOST_USER_GET_VRING_BASE] = "VHOST_GET_VRING_BASE", 243 [VHOST_USER_SET_VRING_ADDR] = "VHOST_SET_VRING_ADDR", 244 [VHOST_USER_SET_VRING_KICK] = "VHOST_SET_VRING_KICK", 245 [VHOST_USER_SET_MEM_TABLE] = "VHOST_SET_MEM_TABLE", 246 [VHOST_USER_SET_VRING_ENABLE] = "VHOST_SET_VRING_ENABLE", 247 }; 248 249 static int 250 vhost_user_sock(struct virtio_user_dev *dev, 251 enum vhost_user_request req, 252 void *arg) 253 { 254 struct vhost_user_msg msg; 255 struct vhost_vring_file *file = 0; 256 int need_reply = 0; 257 int fds[VHOST_MEMORY_MAX_NREGIONS]; 258 int fd_num = 0; 259 int i, len; 260 int vhostfd = dev->vhostfd; 261 262 RTE_SET_USED(m); 263 264 PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]); 265 266 msg.request = req; 267 msg.flags = VHOST_USER_VERSION; 268 msg.size = 0; 269 270 switch (req) { 271 case VHOST_USER_GET_FEATURES: 272 need_reply = 1; 273 break; 274 275 case VHOST_USER_SET_FEATURES: 276 case VHOST_USER_SET_LOG_BASE: 277 msg.payload.u64 = *((__u64 *)arg); 278 msg.size = sizeof(m.payload.u64); 279 break; 280 281 case VHOST_USER_SET_OWNER: 282 case VHOST_USER_RESET_OWNER: 283 break; 284 285 case VHOST_USER_SET_MEM_TABLE: 286 if (prepare_vhost_memory_user(&msg, fds) < 0) 287 return -1; 288 fd_num = msg.payload.memory.nregions; 289 msg.size = sizeof(m.payload.memory.nregions); 290 msg.size += sizeof(m.payload.memory.padding); 291 msg.size += fd_num * sizeof(struct vhost_memory_region); 292 break; 293 294 case VHOST_USER_SET_LOG_FD: 295 fds[fd_num++] = *((int *)arg); 296 break; 297 298 case VHOST_USER_SET_VRING_NUM: 299 case VHOST_USER_SET_VRING_BASE: 300 case VHOST_USER_SET_VRING_ENABLE: 301 memcpy(&msg.payload.state, arg, sizeof(msg.payload.state)); 302 msg.size = sizeof(m.payload.state); 303 break; 304 305 case VHOST_USER_GET_VRING_BASE: 306 memcpy(&msg.payload.state, arg, sizeof(msg.payload.state)); 307 msg.size = sizeof(m.payload.state); 308 need_reply = 1; 309 break; 310 311 case VHOST_USER_SET_VRING_ADDR: 312 memcpy(&msg.payload.addr, arg, sizeof(msg.payload.addr)); 313 msg.size = sizeof(m.payload.addr); 314 break; 315 316 case VHOST_USER_SET_VRING_KICK: 317 case VHOST_USER_SET_VRING_CALL: 318 case VHOST_USER_SET_VRING_ERR: 319 file = arg; 320 msg.payload.u64 = file->index & VHOST_USER_VRING_IDX_MASK; 321 msg.size = sizeof(m.payload.u64); 322 if (file->fd > 0) 323 fds[fd_num++] = file->fd; 324 else 325 msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK; 326 break; 327 328 default: 329 PMD_DRV_LOG(ERR, "trying to send unhandled msg type"); 330 return -1; 331 } 332 333 len = VHOST_USER_HDR_SIZE + msg.size; 334 if (vhost_user_write(vhostfd, &msg, len, fds, fd_num) < 0) { 335 PMD_DRV_LOG(ERR, "%s failed: %s", 336 vhost_msg_strings[req], strerror(errno)); 337 return -1; 338 } 339 340 if (req == VHOST_USER_SET_MEM_TABLE) 341 for (i = 0; i < fd_num; ++i) 342 close(fds[i]); 343 344 if (need_reply) { 345 if (vhost_user_read(vhostfd, &msg) < 0) { 346 PMD_DRV_LOG(ERR, "Received msg failed: %s", 347 strerror(errno)); 348 return -1; 349 } 350 351 if (req != msg.request) { 352 PMD_DRV_LOG(ERR, "Received unexpected msg type"); 353 return -1; 354 } 355 356 switch (req) { 357 case VHOST_USER_GET_FEATURES: 358 if (msg.size != sizeof(m.payload.u64)) { 359 PMD_DRV_LOG(ERR, "Received bad msg size"); 360 return -1; 361 } 362 *((__u64 *)arg) = msg.payload.u64; 363 break; 364 case VHOST_USER_GET_VRING_BASE: 365 if (msg.size != sizeof(m.payload.state)) { 366 PMD_DRV_LOG(ERR, "Received bad msg size"); 367 return -1; 368 } 369 memcpy(arg, &msg.payload.state, 370 sizeof(struct vhost_vring_state)); 371 break; 372 default: 373 PMD_DRV_LOG(ERR, "Received unexpected msg type"); 374 return -1; 375 } 376 } 377 378 return 0; 379 } 380 381 /** 382 * Set up environment to talk with a vhost user backend. 383 * 384 * @return 385 * - (-1) if fail; 386 * - (0) if succeed. 387 */ 388 static int 389 vhost_user_setup(struct virtio_user_dev *dev) 390 { 391 int fd; 392 int flag; 393 struct sockaddr_un un; 394 395 fd = socket(AF_UNIX, SOCK_STREAM, 0); 396 if (fd < 0) { 397 PMD_DRV_LOG(ERR, "socket() error, %s", strerror(errno)); 398 return -1; 399 } 400 401 flag = fcntl(fd, F_GETFD); 402 if (fcntl(fd, F_SETFD, flag | FD_CLOEXEC) < 0) 403 PMD_DRV_LOG(WARNING, "fcntl failed, %s", strerror(errno)); 404 405 memset(&un, 0, sizeof(un)); 406 un.sun_family = AF_UNIX; 407 snprintf(un.sun_path, sizeof(un.sun_path), "%s", dev->path); 408 if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) { 409 PMD_DRV_LOG(ERR, "connect error, %s", strerror(errno)); 410 close(fd); 411 return -1; 412 } 413 414 dev->vhostfd = fd; 415 return 0; 416 } 417 418 static int 419 vhost_user_enable_queue_pair(struct virtio_user_dev *dev, 420 uint16_t pair_idx, 421 int enable) 422 { 423 int i; 424 425 for (i = 0; i < 2; ++i) { 426 struct vhost_vring_state state = { 427 .index = pair_idx * 2 + i, 428 .num = enable, 429 }; 430 431 if (vhost_user_sock(dev, VHOST_USER_SET_VRING_ENABLE, &state)) 432 return -1; 433 } 434 435 return 0; 436 } 437 438 struct virtio_user_backend_ops ops_user = { 439 .setup = vhost_user_setup, 440 .send_request = vhost_user_sock, 441 .enable_qp = vhost_user_enable_queue_pair 442 }; 443