here is a function for net_client_init_fun structure for function pointers.
static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])(
const NetClientOptions *opts,
const char *name,
NetClientState *peer) = {
[NET_CLIENT_OPTIONS_KIND_NIC] = net_init_nic,
#ifdef CONFIG_SLIRP
[NET_CLIENT_OPTIONS_KIND_USER] = net_init_slirp,
#endif
[NET_CLIENT_OPTIONS_KIND_TAP] = net_init_tap,
[NET_CLIENT_OPTIONS_KIND_SOCKET] = net_init_socket,
#ifdef CONFIG_VDE
[NET_CLIENT_OPTIONS_KIND_VDE] = net_init_vde,
#endif
[NET_CLIENT_OPTIONS_KIND_DUMP] = net_init_dump,
#ifdef CONFIG_NET_BRIDGE
[NET_CLIENT_OPTIONS_KIND_BRIDGE] = net_init_bridge,
#endif
[NET_CLIENT_OPTIONS_KIND_HUBPORT] = net_init_hubport,
};
im going to follow a function, net_init_bridge one of them above.
int net_init_bridge(const NetClientOptions *opts, const char *name,
NetClientState *peer)
{
const NetdevBridgeOptions *bridge;
const char *helper, *br;
TAPState *s;
int fd, vnet_hdr;
assert(opts->kind == NET_CLIENT_OPTIONS_KIND_BRIDGE);
bridge = opts->bridge;
helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER;
br = bridge->has_br ? bridge->br : DEFAULT_BRIDGE_INTERFACE;
fd = net_bridge_run_helper(helper, br);
if (fd == -1) {
return -1;
}
fcntl(fd, F_SETFL, O_NONBLOCK);
vnet_hdr = tap_probe_vnet_hdr(fd);
s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr);
if (!s) {
close(fd);
return -1;
}
snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s", helper,
br);
return 0;
}
static TAPState *net_tap_fd_init(NetClientState *peer,
const char *model,
const char *name,
int fd,
int vnet_hdr)
{
NetClientState *nc;
TAPState *s;
nc = qemu_new_net_client(&net_tap_info, peer, model, name);
s = DO_UPCAST(TAPState, nc, nc);
s->fd = fd;
s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;
s->using_vnet_hdr = false;
s->has_ufo = tap_probe_has_ufo(s->fd);
s->enabled = true;
tap_set_offload(&s->nc, 0, 0, 0, 0, 0);
/*
* Make sure host header length is set correctly in tap:
* it might have been modified by another instance of qemu.
*/
if (tap_probe_vnet_hdr_len(s->fd, s->host_vnet_hdr_len)) {
tap_fd_set_vnet_hdr_len(s->fd, s->host_vnet_hdr_len);
}
tap_read_poll(s, true);
s->vhost_net = NULL;
return s;
}
static NetClientInfo net_tap_info = {
.type = NET_CLIENT_OPTIONS_KIND_TAP,
.size = sizeof(TAPState),
.receive = tap_receive,
.receive_raw = tap_receive_raw,
.receive_iov = tap_receive_iov,
.poll = tap_poll,
.cleanup = tap_cleanup,
};
static void tap_read_poll(TAPState *s, bool enable)
{
s->read_poll = enable;
tap_update_fd_handler(s);
}
static void tap_update_fd_handler(TAPState *s)
{
qemu_set_fd_handler2(s->fd,
s->read_poll && s->enabled ? tap_can_send : NULL,
s->read_poll && s->enabled ? tap_send : NULL,
s->write_poll && s->enabled ? tap_writable : NULL,
s);
}
int qemu_set_fd_handler2(int fd,
IOCanReadHandler *fd_read_poll,
IOHandler *fd_read,
IOHandler *fd_write,
void *opaque)
{
IOHandlerRecord *ioh;
assert(fd >= 0);
if (!fd_read && !fd_write) {
QLIST_FOREACH(ioh, &io_handlers, next) {
if (ioh->fd == fd) {
ioh->deleted = 1;
break;
}
}
} else {
QLIST_FOREACH(ioh, &io_handlers, next) {
if (ioh->fd == fd)
goto found;
}
ioh = g_malloc0(sizeof(IOHandlerRecord));
QLIST_INSERT_HEAD(&io_handlers, ioh, next);
found:
ioh->fd = fd;
ioh->fd_read_poll = fd_read_poll;
ioh->fd_read = fd_read;
ioh->fd_write = fd_write;
ioh->opaque = opaque;
ioh->pollfds_idx = -1;
ioh->deleted = 0;
qemu_notify_event();
}
return 0;
}
so,
tap_can_send, tap_send, tap_writable, these are for read and write for network bridge which qemu made for communicating with guest OS.
now, we are curious about where ioh would be called.
The answer is main_loop in vl.c
static void main_loop(void)
{
bool nonblocking;
int last_io = 0;
#ifdef CONFIG_PROFILER
int64_t ti;
#endif
do {
nonblocking = !kvm_enabled() && !xen_enabled() && last_io > 0;
#ifdef CONFIG_PROFILER
ti = profile_getclock();
#endif
last_io = main_loop_wait(nonblocking);
#ifdef CONFIG_PROFILER
dev_time += profile_getclock() - ti;
#endif
} while (!main_loop_should_exit());
}
int main_loop_wait(int nonblocking)
{
int ret;
uint32_t timeout = UINT32_MAX;
int64_t timeout_ns;
if (nonblocking) {
timeout = 0;
}
/* poll any events */
g_array_set_size(gpollfds, 0); /* reset for new iteration */
/* XXX: separate device handlers from system ones */
#ifdef CONFIG_SLIRP
slirp_pollfds_fill(gpollfds, &timeout);
#endif
qemu_iohandler_fill(gpollfds);
if (timeout == UINT32_MAX) {
timeout_ns = -1;
} else {
timeout_ns = (uint64_t)timeout * (int64_t)(SCALE_MS);
}
timeout_ns = qemu_soonest_timeout(timeout_ns,
timerlistgroup_deadline_ns(
&main_loop_tlg));
ret = os_host_main_loop_wait(timeout_ns);
qemu_iohandler_poll(gpollfds, ret);
#ifdef CONFIG_SLIRP
slirp_pollfds_poll(gpollfds, (ret < 0));
#endif
qemu_clock_run_all_timers();
return ret;
}
void qemu_iohandler_poll(GArray *pollfds, int ret)
{
if (ret > 0) {
IOHandlerRecord *pioh, *ioh;
QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) {
int revents = 0;
if (!ioh->deleted && ioh->pollfds_idx != -1) {
GPollFD *pfd = &g_array_index(pollfds, GPollFD,
ioh->pollfds_idx);
revents = pfd->revents;
}
if (!ioh->deleted && ioh->fd_read &&
(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) {
ioh->fd_read(ioh->opaque);
}
if (!ioh->deleted && ioh->fd_write &&
(revents & (G_IO_OUT | G_IO_ERR))) {
ioh->fd_write(ioh->opaque);
}
/* Do this last in case read/write handlers marked it for deletion */
if (ioh->deleted) {
QLIST_REMOVE(ioh, next);
g_free(ioh);
}
}
}
}
'Virtualization' 카테고리의 다른 글
KVM - QEMU Network Initialization 3 (0) | 2014.05.28 |
---|---|
KVM - QEMU Network Initialization 1 (0) | 2014.05.28 |
Initializing process for USB (0) | 2014.05.23 |
link between IDE PortIO and Drive Operation (0) | 2014.05.23 |
Process of Device Initializing in QEMU (0) | 2014.05.23 |