]>
git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/dpdk/examples/vhost_xen/vhost_monitor.c
4 * Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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.
38 #include <sys/eventfd.h>
39 #include <sys/ioctl.h>
41 #include <xen/xen-compat.h>
42 #if __XEN_LATEST_INTERFACE_VERSION__ < 0x00040200
47 #include <linux/virtio_ring.h>
48 #include <linux/virtio_pci.h>
49 #include <linux/virtio_net.h>
51 #include <rte_ethdev.h>
53 #include <rte_malloc.h>
54 #include <rte_string_fns.h>
56 #include "virtio-net.h"
57 #include "xen_vhost.h"
65 /* device ops to add/remove device to/from data core. */
66 static struct virtio_net_device_ops
const *notify_ops
;
68 /* root address of the linked list in the configuration core. */
69 static struct virtio_net_config_ll
*ll_root
= NULL
;
71 /* root address of VM. */
72 static struct xen_guestlist guest_root
;
74 static struct virtio_watch watch
;
77 vq_vring_init(struct vhost_virtqueue
*vq
, unsigned int num
, uint8_t *p
,
81 vq
->desc
= (struct vring_desc
*) p
;
82 vq
->avail
= (struct vring_avail
*) (p
+
83 num
* sizeof(struct vring_desc
));
85 RTE_ALIGN_CEIL( (uintptr_t)(&vq
->avail
->ring
[num
]), align
);
96 /* get a connection to the daemon */
97 xs
= xs_daemon_open();
99 RTE_LOG(ERR
, XENHOST
, "xs_daemon_open failed\n");
103 ret
= xs_watch(xs
, "/local/domain", "mytoken");
105 RTE_LOG(ERR
, XENHOST
, "%s: xs_watch failed\n", __func__
);
110 /* We are notified of read availability on the watch via the file descriptor. */
115 TAILQ_INIT(&guest_root
);
119 static struct xen_guest
*
120 get_xen_guest(int dom_id
)
122 struct xen_guest
*guest
= NULL
;
124 TAILQ_FOREACH(guest
, &guest_root
, next
) {
125 if(guest
->dom_id
== dom_id
)
133 static struct xen_guest
*
134 add_xen_guest(int32_t dom_id
)
136 struct xen_guest
*guest
= NULL
;
138 if ((guest
= get_xen_guest(dom_id
)) != NULL
)
141 guest
= calloc(1, sizeof(struct xen_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
;
152 cleanup_device(struct virtio_net_config_ll
*ll_dev
)
156 if (ll_dev
->dev
.virtqueue_rx
) {
157 rte_free(ll_dev
->dev
.virtqueue_rx
);
158 ll_dev
->dev
.virtqueue_rx
= NULL
;
160 if (ll_dev
->dev
.virtqueue_tx
) {
161 rte_free(ll_dev
->dev
.virtqueue_tx
);
162 ll_dev
->dev
.virtqueue_tx
= NULL
;
168 * Add entry containing a device to the device configuration linked list.
171 add_config_ll_entry(struct virtio_net_config_ll
*new_ll_dev
)
173 struct virtio_net_config_ll
*ll_dev
= ll_root
;
175 /* If ll_dev == NULL then this is the first device so go to else */
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
;
183 /* increment through the ll until we find un unused device_id,
184 * insert the device at that entry
186 while ((ll_dev
->next
!= NULL
) && (ll_dev
->dev
.device_fh
== (ll_dev
->next
->dev
.device_fh
- 1)))
187 ll_dev
= ll_dev
->next
;
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
;
194 ll_root
= new_ll_dev
;
195 ll_root
->dev
.device_fh
= 0;
201 * Remove an entry from the device configuration linked list.
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
)
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
);
212 ll_dev_last
->next
= ll_dev
->next
;
213 cleanup_device(ll_dev
);
214 return ll_dev_last
->next
;
219 * Retrieves an entry from the devices configuration linked list.
221 static struct virtio_net_config_ll
*
222 get_config_ll_entry(unsigned int virtio_idx
, unsigned int dom_id
)
224 struct virtio_net_config_ll
*ll_dev
= ll_root
;
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
)
230 ll_dev
= ll_dev
->next
;
237 * Initialise all variables in device structure.
240 init_dev(struct virtio_net
*dev
)
247 virtio_net_config_ll
*new_device(unsigned int virtio_idx
, struct xen_guest
*guest
)
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
;
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
)
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
);
270 size
= vring_size(vq_size
, VIRTIO_PCI_VRING_ALIGN
);
271 vq_ring_size
= RTE_ALIGN_CEIL(size
, VIRTIO_PCI_VRING_ALIGN
);
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
);
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
));
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
=
289 (uint64_t)((uintptr_t)guest
->mempool
[i
].gva
);
290 new_ll_dev
->dev
.mem
->regions
[i
].guest_phys_address_end
=
291 gpa
+ guest
->mempool
[i
].mempfn_num
* getpagesize();
292 new_ll_dev
->dev
.mem
->regions
[i
].address_offset
=
293 (uint64_t)((uintptr_t)guest
->mempool
[i
].hva
-
297 new_ll_dev
->next
= NULL
;
299 /* Add entry to device configuration linked list. */
300 add_config_ll_entry(new_ll_dev
);
304 rte_free(virtqueue_rx
);
305 rte_free(virtqueue_tx
);
311 destroy_guest(struct xen_guest
*guest
)
315 for (i
= 0; i
< guest
->vring_num
; i
++)
316 cleanup_vring(&guest
->vring
[i
]);
318 for (i
= 0; i
< guest
->pool_num
; i
++)
319 cleanup_mempool(&guest
->mempool
[i
]);
326 * This function will cleanup the device and remove it from device configuration linked list.
329 destroy_device(unsigned int virtio_idx
, unsigned int dom_id
)
331 struct virtio_net_config_ll
*ll_dev_cur_ctx
, *ll_dev_last
= NULL
;
332 struct virtio_net_config_ll
*ll_dev_cur
= ll_root
;
334 /* clean virtio device */
335 struct xen_guest
*guest
= NULL
;
336 guest
= get_xen_guest(dom_id
);
340 /* Find the linked list entry for the device to be removed. */
341 ll_dev_cur_ctx
= get_config_ll_entry(virtio_idx
, dom_id
);
342 while (ll_dev_cur
!= NULL
) {
343 /* If the device is found or a device that doesn't exist is found then it is removed. */
344 if (ll_dev_cur
== ll_dev_cur_ctx
) {
345 if ((ll_dev_cur
->dev
.flags
& VIRTIO_DEV_RUNNING
))
346 notify_ops
->destroy_device(&(ll_dev_cur
->dev
));
347 ll_dev_cur
= rm_config_ll_entry(ll_dev_cur
, ll_dev_last
);
349 ll_dev_last
= ll_dev_cur
;
350 ll_dev_cur
= ll_dev_cur
->next
;
353 RTE_LOG(INFO
, XENHOST
, " %s guest:%p vring:%p rxvring:%p txvring:%p flag:%p\n",
354 __func__
, guest
, &guest
->vring
[virtio_idx
], guest
->vring
[virtio_idx
].rxvring_addr
, guest
->vring
[virtio_idx
].txvring_addr
, guest
->vring
[virtio_idx
].flag
);
355 cleanup_vring(&guest
->vring
[virtio_idx
]);
356 guest
->vring
[virtio_idx
].removed
= 1;
357 guest
->vring_num
-= 1;
364 watch_unmap_event(void)
367 struct xen_guest
*guest
= NULL
;
370 TAILQ_FOREACH(guest
, &guest_root
, next
) {
371 for (i
= 0; i
< MAX_VIRTIO
; i
++) {
372 if (guest
->vring
[i
].dom_id
&& guest
->vring
[i
].removed
== 0 && *guest
->vring
[i
].flag
== 0) {
373 RTE_LOG(INFO
, XENHOST
, "\n\n");
374 RTE_LOG(INFO
, XENHOST
, " #####%s: (%d, %d) to be removed\n",
376 guest
->vring
[i
].dom_id
,
378 destroy_device(i
, guest
->dom_id
);
379 RTE_LOG(INFO
, XENHOST
, " %s: DOM %u, vring num: %d\n",
389 remove_request
= false;
390 TAILQ_FOREACH(guest
, &guest_root
, next
) {
391 if (guest
->vring_num
== 0) {
392 remove_request
= true;
396 if (remove_request
== true) {
397 TAILQ_REMOVE(&guest_root
, guest
, next
);
398 RTE_LOG(INFO
, XENHOST
, " #####%s: destroy guest (%d)\n", __func__
, guest
->dom_id
);
399 destroy_guest(guest
);
400 goto _find_next_remove
;
406 * OK, if the guest starts first, it is ok.
407 * if host starts first, it is ok.
408 * if guest starts, and has run for sometime, and host stops and restarts,
409 * then last_used_idx 0? how to solve this. */
411 static void virtio_init(void)
421 struct xen_guest
*guest
;
422 struct virtio_net_config_ll
*net_config
;
426 /* init env for watch the node */
427 if (init_watch() < 0)
430 dom
= xs_directory(watch
.xs
, XBT_NULL
, "/local/domain", &e_num
);
432 for (i
= 0; i
< e_num
; i
++) {
434 dom_id
= strtol(dom
[i
], &end
, 0);
435 if (errno
!= 0 || end
== NULL
|| dom_id
== 0)
438 for (j
= 0; j
< RTE_MAX_ETHPORTS
; j
++) {
439 snprintf(node
, PATH_MAX
, "%s%d", VIRTIO_START
, j
);
440 snprintf(path
, PATH_MAX
, XEN_VM_NODE_FMT
,
443 th
= xs_transaction_start(watch
.xs
);
444 status
= xs_read(watch
.xs
, th
, path
, &len
);
445 xs_transaction_end(watch
.xs
, th
, false);
450 /* if there's any valid virtio device */
452 val
= strtol(status
, &end
, 0);
453 if (errno
!= 0 || end
== NULL
|| dom_id
== 0)
456 guest
= add_xen_guest(dom_id
);
459 RTE_LOG(INFO
, XENHOST
, " there's a new virtio existed, new a virtio device\n\n");
461 RTE_LOG(INFO
, XENHOST
, " parse_vringnode dom_id %d virtioidx %d\n",dom_id
,j
);
462 if (parse_vringnode(guest
, j
)) {
463 RTE_LOG(ERR
, XENHOST
, " there is invalid information in xenstore\n");
464 TAILQ_REMOVE(&guest_root
, guest
, next
);
465 destroy_guest(guest
);
470 /*if pool_num > 0, then mempool has already been parsed*/
471 if (guest
->pool_num
== 0 && parse_mempoolnode(guest
)) {
472 RTE_LOG(ERR
, XENHOST
, " there is error information in xenstore\n");
473 TAILQ_REMOVE(&guest_root
, guest
, next
);
474 destroy_guest(guest
);
478 net_config
= new_device(j
, guest
);
479 /* every thing is ready now, added into data core */
480 notify_ops
->new_device(&net_config
->dev
);
490 virtio_monitor_loop(void)
498 struct xen_guest
*guest
;
499 struct virtio_net_config_ll
*net_config
;
510 char *str_fld
[_NUM_FLD
];
519 vec
= xs_check_watch(watch
.xs
);
524 th
= xs_transaction_start(watch
.xs
);
526 buf
= xs_read(watch
.xs
, th
, vec
[XS_WATCH_PATH
],&len
);
527 xs_transaction_end(watch
.xs
, th
, false);
530 /* theres' some node for vhost existed */
531 if (rte_strsplit(vec
[XS_WATCH_PATH
], strnlen(vec
[XS_WATCH_PATH
], PATH_MAX
),
532 str_fld
, _NUM_FLD
, '/') == _NUM_FLD
) {
533 if (strstr(str_fld
[FLD_NODE
], VIRTIO_START
)) {
535 str
= str_fld
[FLD_ID
];
536 dom_id
= strtoul(str
, &end
, 0);
537 if (errno
!= 0 || end
== NULL
|| end
== str
) {
538 RTE_LOG(INFO
, XENHOST
, "invalid domain id\n");
543 str
= str_fld
[FLD_NODE
] + sizeof(VIRTIO_START
) - 1;
544 virtio_idx
= strtoul(str
, &end
, 0);
545 if (errno
!= 0 || end
== NULL
|| end
== str
546 || virtio_idx
> MAX_VIRTIO
) {
547 RTE_LOG(INFO
, XENHOST
, "invalid virtio idx\n");
550 RTE_LOG(INFO
, XENHOST
, " #####virtio dev (%d, %d) is started\n", dom_id
, virtio_idx
);
552 guest
= add_xen_guest(dom_id
);
555 guest
->dom_id
= dom_id
;
556 if (parse_vringnode(guest
, virtio_idx
)) {
557 RTE_LOG(ERR
, XENHOST
, " there is invalid information in xenstore\n");
558 /*guest newly created? guest existed ?*/
559 TAILQ_REMOVE(&guest_root
, guest
, next
);
560 destroy_guest(guest
);
563 /*if pool_num > 0, then mempool has already been parsed*/
564 if (guest
->pool_num
== 0 && parse_mempoolnode(guest
)) {
565 RTE_LOG(ERR
, XENHOST
, " there is error information in xenstore\n");
566 TAILQ_REMOVE(&guest_root
, guest
, next
);
567 destroy_guest(guest
);
572 net_config
= new_device(virtio_idx
, guest
);
573 RTE_LOG(INFO
, XENHOST
, " Add to dataplane core\n");
574 notify_ops
->new_device(&net_config
->dev
);
586 * Register ops so that we can add/remove device to data core.
589 init_virtio_xen(struct virtio_net_device_ops
const *const ops
)