add prefix to cache line macros
[dpdk.git] / examples / vhost_xen / vhost_monitor.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <dirent.h>
37 #include <unistd.h>
38 #include <sys/eventfd.h>
39 #include <sys/ioctl.h>
40 #include <sys/mman.h>
41 #include <xen/xen-compat.h>
42 #if __XEN_LATEST_INTERFACE_VERSION__ < 0x00040200
43 #include <xs.h>
44 #else
45 #include <xenstore.h>
46 #endif
47 #include <linux/virtio_ring.h>
48 #include <linux/virtio_pci.h>
49 #include <linux/virtio_net.h>
50
51 #include <rte_ethdev.h>
52 #include <rte_log.h>
53 #include <rte_malloc.h>
54 #include <rte_string_fns.h>
55
56 #include "virtio-net.h"
57 #include "xen_vhost.h"
58
59 struct virtio_watch {
60         struct xs_handle *xs;
61         int watch_fd;
62 };
63
64
65 /* device ops to add/remove device to/from data core. */
66 static struct virtio_net_device_ops const *notify_ops;
67
68 /* root address of the linked list in the configuration core. */
69 static struct virtio_net_config_ll *ll_root = NULL;
70
71 /* root address of VM. */
72 static struct xen_guestlist guest_root;
73
74 static struct virtio_watch watch;
75
76 static void
77 vq_vring_init(struct vhost_virtqueue *vq, unsigned int num, uint8_t *p,
78         unsigned long align)
79 {
80         vq->size = num;
81         vq->desc = (struct vring_desc *) p;
82         vq->avail = (struct vring_avail *) (p +
83                 num * sizeof(struct vring_desc));
84         vq->used = (void *)
85                 RTE_ALIGN_CEIL( (uintptr_t)(&vq->avail->ring[num]), align);
86
87 }
88
89 static int
90 init_watch(void)
91 {
92         struct xs_handle *xs;
93         int ret;
94         int fd;
95
96         /* get a connection to the daemon */
97         xs = xs_daemon_open();
98         if (xs == NULL) {
99                 RTE_LOG(ERR, XENHOST, "xs_daemon_open failed\n");
100                 return (-1);
101         }
102
103         ret = xs_watch(xs, "/local/domain", "mytoken");
104         if (ret == 0) {
105                 RTE_LOG(ERR, XENHOST, "%s: xs_watch failed\n", __func__);
106                 xs_daemon_close(xs);
107                 return (-1);
108         }
109
110         /* We are notified of read availability on the watch via the file descriptor. */
111         fd = xs_fileno(xs);
112         watch.xs = xs;
113         watch.watch_fd = fd;
114
115         TAILQ_INIT(&guest_root);
116         return 0;
117 }
118
119 static struct xen_guest *
120 get_xen_guest(int dom_id)
121 {
122         struct xen_guest *guest = NULL;
123
124         TAILQ_FOREACH(guest, &guest_root, next) {
125                 if(guest->dom_id == dom_id)
126                         return guest;
127         }
128
129         return (NULL);
130 }
131
132
133 static struct xen_guest *
134 add_xen_guest(int32_t dom_id)
135 {
136         struct xen_guest *guest = NULL;
137
138         if ((guest = get_xen_guest(dom_id)) != NULL)
139                 return guest;
140
141         guest = (struct xen_guest * )calloc(1, sizeof(struct xen_guest));
142         if (guest) {
143                 RTE_LOG(ERR, XENHOST, "  %s: return newly created guest with %d rings\n", __func__, guest->vring_num);
144                 TAILQ_INSERT_TAIL(&guest_root, guest, next);
145                 guest->dom_id = dom_id;
146         }
147
148         return guest;
149 }
150
151 static void
152 cleanup_device(struct virtio_net_config_ll *ll_dev)
153 {
154         if (ll_dev == NULL)
155                 return;
156         if (ll_dev->dev.virtqueue_rx) {
157                 rte_free(ll_dev->dev.virtqueue_rx);
158                 ll_dev->dev.virtqueue_rx = NULL;
159         }
160         if (ll_dev->dev.virtqueue_tx) {
161                 rte_free(ll_dev->dev.virtqueue_tx);
162                 ll_dev->dev.virtqueue_tx = NULL;
163         }
164         free(ll_dev);
165 }
166
167 /*
168  * Add entry containing a device to the device configuration linked list.
169  */
170 static void
171 add_config_ll_entry(struct virtio_net_config_ll *new_ll_dev)
172 {
173         struct virtio_net_config_ll *ll_dev = ll_root;
174
175         /* If ll_dev == NULL then this is the first device so go to else */
176         if (ll_dev) {
177                 /* If the 1st device_id != 0 then we insert our device here. */
178                 if (ll_dev->dev.device_fh != 0) {
179                         new_ll_dev->dev.device_fh = 0;
180                         new_ll_dev->next = ll_dev;
181                         ll_root = new_ll_dev;
182                 } else {
183                         /* increment through the ll until we find un unused device_id,
184                          * insert the device at that entry
185                          */
186                         while ((ll_dev->next != NULL) && (ll_dev->dev.device_fh == (ll_dev->next->dev.device_fh - 1)))
187                                 ll_dev = ll_dev->next;
188
189                         new_ll_dev->dev.device_fh = ll_dev->dev.device_fh + 1;
190                         new_ll_dev->next = ll_dev->next;
191                         ll_dev->next = new_ll_dev;
192                 }
193         } else {
194                 ll_root = new_ll_dev;
195                 ll_root->dev.device_fh = 0;
196         }
197 }
198
199
200 /*
201  * Remove an entry from the device configuration linked list.
202  */
203 static struct virtio_net_config_ll *
204 rm_config_ll_entry(struct virtio_net_config_ll *ll_dev, struct virtio_net_config_ll *ll_dev_last)
205 {
206         /* First remove the device and then clean it up. */
207         if (ll_dev == ll_root) {
208                 ll_root = ll_dev->next;
209                 cleanup_device(ll_dev);
210                 return ll_root;
211         } else {
212                 ll_dev_last->next = ll_dev->next;
213                 cleanup_device(ll_dev);
214                 return ll_dev_last->next;
215         }
216 }
217
218 /*
219  * Retrieves an entry from the devices configuration linked list.
220  */
221 static struct virtio_net_config_ll *
222 get_config_ll_entry(unsigned int virtio_idx, unsigned int dom_id)
223 {
224         struct virtio_net_config_ll *ll_dev = ll_root;
225
226         /* Loop through linked list until the dom_id is found. */
227         while (ll_dev != NULL) {
228                 if (ll_dev->dev.dom_id == dom_id && ll_dev->dev.virtio_idx == virtio_idx)
229                         return ll_dev;
230                 ll_dev = ll_dev->next;
231         }
232
233         return NULL;
234 }
235
236 /*
237  * Initialise all variables in device structure.
238  */
239 static void
240 init_dev(struct virtio_net *dev)
241 {
242         RTE_SET_USED(dev);
243 }
244
245
246 static struct
247 virtio_net_config_ll *new_device(unsigned int virtio_idx, struct xen_guest *guest)
248 {
249         struct virtio_net_config_ll *new_ll_dev;
250         struct vhost_virtqueue *virtqueue_rx, *virtqueue_tx;
251         size_t size, vq_ring_size, vq_size = VQ_DESC_NUM;
252         void *vq_ring_virt_mem;
253         uint64_t gpa;
254         uint32_t i;
255
256         /* Setup device and virtqueues. */
257         new_ll_dev   = calloc(1, sizeof(struct virtio_net_config_ll));
258         virtqueue_rx = rte_zmalloc(NULL, sizeof(struct vhost_virtqueue), RTE_CACHE_LINE_SIZE);
259         virtqueue_tx = rte_zmalloc(NULL, sizeof(struct vhost_virtqueue), RTE_CACHE_LINE_SIZE);
260         if (new_ll_dev == NULL || virtqueue_rx == NULL || virtqueue_tx == NULL)
261                 goto err;
262
263         new_ll_dev->dev.virtqueue_rx = virtqueue_rx;
264         new_ll_dev->dev.virtqueue_tx = virtqueue_tx;
265         new_ll_dev->dev.dom_id       = guest->dom_id;
266         new_ll_dev->dev.virtio_idx   = virtio_idx;
267         /* Initialise device and virtqueues. */
268         init_dev(&new_ll_dev->dev);
269
270         size = vring_size(vq_size, VIRTIO_PCI_VRING_ALIGN);
271         vq_ring_size = RTE_ALIGN_CEIL(size, VIRTIO_PCI_VRING_ALIGN);
272         (void)vq_ring_size;
273
274         vq_ring_virt_mem = guest->vring[virtio_idx].rxvring_addr;
275         vq_vring_init(virtqueue_rx, vq_size, vq_ring_virt_mem, VIRTIO_PCI_VRING_ALIGN);
276         virtqueue_rx->size = vq_size;
277         virtqueue_rx->vhost_hlen = sizeof(struct virtio_net_hdr);
278
279         vq_ring_virt_mem = guest->vring[virtio_idx].txvring_addr;
280         vq_vring_init(virtqueue_tx, vq_size, vq_ring_virt_mem, VIRTIO_PCI_VRING_ALIGN);
281         virtqueue_tx->size = vq_size;
282         memcpy(&new_ll_dev->dev.mac_address, &guest->vring[virtio_idx].addr, sizeof(struct ether_addr));
283
284         /* virtio_memory has to be one per domid */
285         new_ll_dev->dev.mem = malloc(sizeof(struct virtio_memory) + sizeof(struct virtio_memory_regions) * MAX_XENVIRT_MEMPOOL);
286         new_ll_dev->dev.mem->nregions = guest->pool_num;
287         for (i = 0; i < guest->pool_num; i++) {
288                 gpa = new_ll_dev->dev.mem->regions[i].guest_phys_address = (uint64_t)guest->mempool[i].gva;
289                 new_ll_dev->dev.mem->regions[i].guest_phys_address_end = gpa + guest->mempool[i].mempfn_num * getpagesize();
290                 new_ll_dev->dev.mem->regions[i].address_offset = (uint64_t)guest->mempool[i].hva - gpa;
291         }
292
293         new_ll_dev->next = NULL;
294
295         /* Add entry to device configuration linked list. */
296         add_config_ll_entry(new_ll_dev);
297         return new_ll_dev;
298 err:
299         if (new_ll_dev)
300                 free(new_ll_dev);
301         if (virtqueue_rx)
302                 rte_free(virtqueue_rx);
303         if (virtqueue_tx)
304                 rte_free(virtqueue_tx);
305         return NULL;
306 }
307
308 static void
309 destroy_guest(struct xen_guest *guest)
310 {
311         uint32_t i;
312
313         for (i = 0; i < guest->vring_num; i++)
314                 cleanup_vring(&guest->vring[i]);
315         /* clean mempool */
316         for (i = 0; i < guest->pool_num; i++)
317                 cleanup_mempool(&guest->mempool[i]);
318         free(guest);
319
320         return;
321 }
322
323 /*
324  * This function will cleanup the device and remove it from device configuration linked list.
325  */
326 static void
327 destroy_device(unsigned int virtio_idx, unsigned int dom_id)
328 {
329         struct virtio_net_config_ll *ll_dev_cur_ctx, *ll_dev_last = NULL;
330         struct virtio_net_config_ll *ll_dev_cur = ll_root;
331
332         /* clean virtio device */
333         struct xen_guest *guest = NULL;
334         guest = get_xen_guest(dom_id);
335         if (guest == NULL)
336                 return;
337
338         /* Find the linked list entry for the device to be removed. */
339         ll_dev_cur_ctx = get_config_ll_entry(virtio_idx, dom_id);
340         while (ll_dev_cur != NULL) {
341                 /* If the device is found or a device that doesn't exist is found then it is removed. */
342                 if  (ll_dev_cur == ll_dev_cur_ctx) {
343                         if ((ll_dev_cur->dev.flags & VIRTIO_DEV_RUNNING))
344                                 notify_ops->destroy_device(&(ll_dev_cur->dev));
345                         ll_dev_cur = rm_config_ll_entry(ll_dev_cur, ll_dev_last);
346                 } else {
347                         ll_dev_last = ll_dev_cur;
348                         ll_dev_cur = ll_dev_cur->next;
349                 }
350         }
351         RTE_LOG(INFO, XENHOST, "  %s guest:%p vring:%p rxvring:%p txvring:%p flag:%p\n",
352                 __func__, guest, &guest->vring[virtio_idx], guest->vring[virtio_idx].rxvring_addr, guest->vring[virtio_idx].txvring_addr, guest->vring[virtio_idx].flag);
353         cleanup_vring(&guest->vring[virtio_idx]);
354         guest->vring[virtio_idx].removed = 1;
355         guest->vring_num -= 1;
356 }
357
358
359
360
361 static void
362 watch_unmap_event(void)
363 {
364         int i;
365         struct xen_guest *guest  = NULL;
366         bool remove_request;
367
368         TAILQ_FOREACH(guest, &guest_root, next) {
369                 for (i = 0; i < MAX_VIRTIO; i++) {
370                         if (guest->vring[i].dom_id && guest->vring[i].removed == 0 && *guest->vring[i].flag == 0) {
371                                 RTE_LOG(INFO, XENHOST, "\n\n");
372                                 RTE_LOG(INFO, XENHOST, "  #####%s:  (%d, %d) to be removed\n",
373                                         __func__,
374                                         guest->vring[i].dom_id,
375                                         i);
376                                 destroy_device(i, guest->dom_id);
377                                 RTE_LOG(INFO, XENHOST, "  %s: DOM %u, vring num: %d\n",
378                                         __func__,
379                                         guest->dom_id,
380                                         guest->vring_num);
381                         }
382                 }
383         }
384
385 _find_next_remove:
386         guest = NULL;
387         remove_request = false;
388         TAILQ_FOREACH(guest, &guest_root, next) {
389                 if (guest->vring_num == 0) {
390                         remove_request = true;
391                         break;
392                 }
393         }
394         if (remove_request == true) {
395                 TAILQ_REMOVE(&guest_root, guest, next);
396                 RTE_LOG(INFO, XENHOST, "  #####%s: destroy guest (%d)\n", __func__, guest->dom_id);
397                 destroy_guest(guest);
398                 goto _find_next_remove;
399         }
400         return;
401 }
402
403 /*
404  * OK, if the guest starts first, it is ok.
405  * if host starts first, it is ok.
406  * if guest starts, and has run for sometime, and host stops and restarts,
407  * then last_used_idx  0? how to solve this. */
408
409 static void virtio_init(void)
410 {
411         uint32_t len, e_num;
412         uint32_t i,j;
413         char **dom;
414         char *status;
415         int dom_id;
416         char path[PATH_MAX];
417         char node[PATH_MAX];
418         xs_transaction_t th;
419         struct xen_guest *guest;
420         struct virtio_net_config_ll *net_config;
421         char *end;
422         int val;
423
424         /* init env for watch the node */
425         if (init_watch() < 0)
426                 return;
427
428         dom = xs_directory(watch.xs, XBT_NULL, "/local/domain", &e_num);
429
430         for (i = 0; i < e_num; i++) {
431                 errno = 0;
432                 dom_id = strtol(dom[i], &end, 0);
433                 if (errno != 0 || end == NULL || dom_id == 0)
434                         continue;
435
436                 for (j = 0; j < RTE_MAX_ETHPORTS; j++) {
437                         snprintf(node, PATH_MAX, "%s%d", VIRTIO_START, j);
438                         snprintf(path, PATH_MAX, XEN_VM_NODE_FMT,
439                                         dom_id, node);
440
441                         th = xs_transaction_start(watch.xs);
442                         status = xs_read(watch.xs, th, path, &len);
443                         xs_transaction_end(watch.xs, th, false);
444
445                         if (status == NULL)
446                                 break;
447
448                         /* if there's any valid virtio device */
449                         errno = 0;
450                         val = strtol(status, &end, 0);
451                         if (errno != 0 || end == NULL || dom_id == 0)
452                                 val = 0;
453                         if (val == 1) {
454                                 guest = add_xen_guest(dom_id);
455                                 if (guest == NULL)
456                                         continue;
457                                 RTE_LOG(INFO, XENHOST, "  there's a new virtio existed, new a virtio device\n\n");
458
459                                 RTE_LOG(INFO, XENHOST, "  parse_vringnode dom_id %d virtioidx %d\n",dom_id,j);
460                                 if (parse_vringnode(guest, j)) {
461                                         RTE_LOG(ERR, XENHOST, "  there is invalid information in xenstore\n");
462                                         TAILQ_REMOVE(&guest_root, guest, next);
463                                         destroy_guest(guest);
464
465                                         continue;
466                                 }
467
468                                 /*if pool_num > 0, then mempool has already been parsed*/
469                                 if (guest->pool_num == 0 && parse_mempoolnode(guest)) {
470                                         RTE_LOG(ERR, XENHOST, "  there is error information in xenstore\n");
471                                         TAILQ_REMOVE(&guest_root, guest, next);
472                                         destroy_guest(guest);
473                                         continue;
474                                 }
475
476                                 net_config = new_device(j, guest);
477                                 /* every thing is ready now, added into data core */
478                                 notify_ops->new_device(&net_config->dev);
479                         }
480                 }
481         }
482
483         free(dom);
484         return;
485 }
486
487 void
488 virtio_monitor_loop(void)
489 {
490         char **vec;
491         xs_transaction_t th;
492         char *buf;
493         unsigned int len;
494         unsigned int dom_id;
495         uint32_t virtio_idx;
496         struct xen_guest *guest;
497         struct virtio_net_config_ll *net_config;
498         enum fieldnames {
499                 FLD_NULL = 0,
500                 FLD_LOCAL,
501                 FLD_DOMAIN,
502                 FLD_ID,
503                 FLD_CONTROL,
504                 FLD_DPDK,
505                 FLD_NODE,
506                 _NUM_FLD
507         };
508         char *str_fld[_NUM_FLD];
509         char *str;
510         char *end;
511
512         virtio_init();
513         while (1) {
514                 watch_unmap_event();
515
516                 usleep(50);
517                 vec = xs_check_watch(watch.xs);
518
519                 if (vec == NULL)
520                         continue;
521
522                 th = xs_transaction_start(watch.xs);
523
524                 buf = xs_read(watch.xs, th, vec[XS_WATCH_PATH],&len);
525                 xs_transaction_end(watch.xs, th, false);
526
527                 if (buf) {
528                         /* theres' some node for vhost existed */
529                         if (rte_strsplit(vec[XS_WATCH_PATH], strnlen(vec[XS_WATCH_PATH], PATH_MAX),
530                                                 str_fld, _NUM_FLD, '/') == _NUM_FLD) {
531                                 if (strstr(str_fld[FLD_NODE], VIRTIO_START)) {
532                                         errno = 0;
533                                         str = str_fld[FLD_ID];
534                                         dom_id = strtoul(str, &end, 0);
535                                         if (errno != 0 || end == NULL || end == str ) {
536                                                 RTE_LOG(INFO, XENHOST, "invalid domain id\n");
537                                                 continue;
538                                         }
539
540                                         errno = 0;
541                                         str = str_fld[FLD_NODE] + sizeof(VIRTIO_START) - 1;
542                                         virtio_idx = strtoul(str, &end, 0);
543                                         if (errno != 0 || end == NULL || end == str
544                                                         || virtio_idx > MAX_VIRTIO) {
545                                                 RTE_LOG(INFO, XENHOST, "invalid virtio idx\n");
546                                                 continue;
547                                         }
548                                         RTE_LOG(INFO, XENHOST, "  #####virtio dev (%d, %d) is started\n", dom_id, virtio_idx);
549
550                                         guest = add_xen_guest(dom_id);
551                                         if (guest == NULL)
552                                                 continue;
553                                         guest->dom_id = dom_id;
554                                         if (parse_vringnode(guest, virtio_idx)) {
555                                                 RTE_LOG(ERR, XENHOST, "  there is invalid information in xenstore\n");
556                                                 /*guest newly created? guest existed ?*/
557                                                 TAILQ_REMOVE(&guest_root, guest, next);
558                                                 destroy_guest(guest);
559                                                 continue;
560                                         }
561                                         /*if pool_num > 0, then mempool has already been parsed*/
562                                         if (guest->pool_num == 0 && parse_mempoolnode(guest)) {
563                                                 RTE_LOG(ERR, XENHOST, "  there is error information in xenstore\n");
564                                                 TAILQ_REMOVE(&guest_root, guest, next);
565                                                 destroy_guest(guest);
566                                                 continue;
567                                         }
568
569
570                                         net_config = new_device(virtio_idx, guest);
571                                         RTE_LOG(INFO, XENHOST, "  Add to dataplane core\n");
572                                         notify_ops->new_device(&net_config->dev);
573
574                                 }
575                         }
576                 }
577
578                 free(vec);
579         }
580         return;
581 }
582
583 /*
584  * Register ops so that we can add/remove device to data core.
585  */
586 int
587 init_virtio_xen(struct virtio_net_device_ops const *const ops)
588 {
589         notify_ops = ops;
590         if (xenhost_init())
591                 return -1;
592         return 0;
593 }
594
595