'Virtualization'에 해당되는 글 23건

  1. 2014.05.28 KVM - QEMU Network Initialization 3 by MeatNBrew
  2. 2014.05.28 KVM - QEMU Network Initialization 2 by MeatNBrew
  3. 2014.05.28 KVM - QEMU Network Initialization 1 by MeatNBrew
  4. 2014.05.23 Initializing process for USB by MeatNBrew
  5. 2014.05.23 link between IDE PortIO and Drive Operation by MeatNBrew
  6. 2014.05.23 Process of Device Initializing in QEMU by MeatNBrew
  7. 2014.05.23 Network Card Initialization of QEMU by MeatNBrew
  8. 2014.05.23 Images for QEMU Inside by MeatNBrew
  9. 2014.05.23 Module Starting Points by MeatNBrew
  10. 2014.05.23 Qemu's Process of Reading or Writing Files by MeatNBrew

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,

};

 

finally all functions for network will call receive_raw or receive. as qemu_deliver_packet function.

ssize_t qemu_deliver_packet(NetClientState *sender,

                            unsigned flags,

                            const uint8_t *data,

                            size_t size,

                            void *opaque)

{

    NetClientState *nc = opaque; // this is a peer's state.

    ssize_t ret;

 

    if (nc->link_down) {

        return size;

    }

 

    if (nc->receive_disabled) {

        return 0;

    }

 

    if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) {

        ret = nc->info->receive_raw(nc, data, size);

    } else {

        ret = nc->info->receive(nc, data, size);

    }

 

    if (ret == 0) {

        nc->receive_disabled = 1;

    };

 

    return ret;

}

 

both tap_receive for receive and tap_receive_raw for receive_raw will call tap_write_packet function.

 

static ssize_t tap_receive_raw(NetClientState *nc, const uint8_t *buf, size_t size)

{

    TAPState *s = DO_UPCAST(TAPState, nc, nc);

    struct iovec iov[2];

    int iovcnt = 0;

    struct virtio_net_hdr_mrg_rxbuf hdr = { };

 

    if (s->host_vnet_hdr_len) {

        iov[iovcnt].iov_base = &hdr;

        iov[iovcnt].iov_len  = s->host_vnet_hdr_len;

        iovcnt++;

    }

 

    iov[iovcnt].iov_base = (char *)buf;

    iov[iovcnt].iov_len  = size;

    iovcnt++;

 

    return tap_write_packet(s, iov, iovcnt);

}

 

static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size)

{

    TAPState *s = DO_UPCAST(TAPState, nc, nc);

    struct iovec iov[1];

 

    if (s->host_vnet_hdr_len && !s->using_vnet_hdr) {

        return tap_receive_raw(nc, buf, size);

    }

 

    iov[0].iov_base = (char *)buf;

    iov[0].iov_len  = size;

 

    return tap_write_packet(s, iov, 1);

}

static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt)

{

    ssize_t len;

 

    do {

        len = writev(s->fd, iov, iovcnt);

    } while (len == -1 && errno == EINTR);

 

    if (len == -1 && errno == EAGAIN) {

        tap_write_poll(s, true);

        return 0;

    }

 

    return len;

}

 

static ssize_t readv_writev(int fd, const struct iovec *iov, int iov_cnt, bool do_write)

{

    unsigned i = 0;

    ssize_t ret = 0;

    while (i < iov_cnt) {

        ssize_t r = do_write

            ? write(fd, iov[i].iov_base, iov[i].iov_len)

            : read(fd, iov[i].iov_base, iov[i].iov_len);

        if (r > 0) {

            ret += r;

        } else if (!r) {

            break;

        } else if (errno == EINTR) {

            continue;

        } else {

            /* else it is some "other" error,

             * only return if there was no data processed. */

            if (ret == 0) {

                ret = -1;

            }

            break;

        }

        i++;

    }

    return ret;

}

 

finally we found file descriptor network bridge use!!.

Posted by MeatNBrew
l

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);

            }

        }

    }

}

Posted by MeatNBrew
l

you can find this code at 4119 line of vl.c

   if (net_init_clients() < 0) {

        exit(1);

    }

and also can find this code  at 188 line of qemu/hw/i386/pc_piix.c

    pc_nic_init(isa_bus, pci_bus); // network card initialization


Those two parts are key function to start initializing Network of Guest and Hypervisor.

Then, let's follow those functions.

First, we are starting with net_init_clients() function.

int net_init_clients(void)

{

    QemuOptsList *net = qemu_find_opts("net");


    if (default_net) {

        /* if no clients, we use a default config */

        qemu_opts_set(net, NULL, "type", "nic");

#ifdef CONFIG_SLIRP

        qemu_opts_set(net, NULL, "type", "user");

#endif

    }


    QTAILQ_INIT(&net_clients);


    if (qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL, 1) == -1)

        return -1;


    if (qemu_opts_foreach(net, net_init_client, NULL, 1) == -1) {

        return -1;

    }


    return 0;

}

those function both net_init_netdev and net_init_client are doing same thing, so we follow net_init_netdev function.( finally both will call net_client_init function.

static int net_init_netdev(QemuOpts *opts, void *dummy)

{

    Error *local_err = NULL;

    int ret;


    ret = net_client_init(opts, 1, &local_err);

    if (error_is_set(&local_err)) {

        qerror_report_err(local_err);

        error_free(local_err);

        return -1;

    }

    return ret;

}

int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)

{

    void *object = NULL;

    Error *err = NULL;

    int ret = -1;


    {

        OptsVisitor *ov = opts_visitor_new(opts);


        net_visit(opts_get_visitor(ov), is_netdev, &object, &err);

        opts_visitor_cleanup(ov);

    }


    if (!err) {

        ret = net_client_init1(object, is_netdev, &err);

    }


    if (object) {

        QapiDeallocVisitor *dv = qapi_dealloc_visitor_new();


        net_visit(qapi_dealloc_get_visitor(dv), is_netdev, &object, NULL);

        qapi_dealloc_visitor_cleanup(dv);

    }


    error_propagate(errp, err);

    return ret;

}


static int net_client_init1(const void *object, int is_netdev, Error **errp)

{

    union {

        const Netdev    *netdev;

        const NetLegacy *net;

    } u;

    const NetClientOptions *opts;

    const char *name;


    if (is_netdev) {

        u.netdev = object;

        opts = u.netdev->opts;

        name = u.netdev->id;


        switch (opts->kind) {

#ifdef CONFIG_SLIRP

        case NET_CLIENT_OPTIONS_KIND_USER:

#endif

        case NET_CLIENT_OPTIONS_KIND_TAP:

        case NET_CLIENT_OPTIONS_KIND_SOCKET:

#ifdef CONFIG_VDE

        case NET_CLIENT_OPTIONS_KIND_VDE:

#endif

#ifdef CONFIG_NET_BRIDGE

        case NET_CLIENT_OPTIONS_KIND_BRIDGE:

#endif

        case NET_CLIENT_OPTIONS_KIND_HUBPORT:

            break;


        default:

            error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",

                      "a netdev backend type");

            return -1;

        }

    } else {

        u.net = object;

        opts = u.net->opts;

        /* missing optional values have been initialized to "all bits zero" */

        name = u.net->has_id ? u.net->id : u.net->name;

    }


    if (net_client_init_fun[opts->kind]) {

        NetClientState *peer = NULL;


        /* Do not add to a vlan if it's a -netdev or a nic with a netdev=

         * parameter. */

        if (!is_netdev &&

            (opts->kind != NET_CLIENT_OPTIONS_KIND_NIC ||

             !opts->nic->has_netdev)) {

            peer = net_hub_add_port(u.net->has_vlan ? u.net->vlan : 0, NULL);

        }


        if (net_client_init_fun[opts->kind](opts, name, peer) < 0) {

            /* TODO push error reporting into init() methods */

            error_set(errp, QERR_DEVICE_INIT_FAILED,

                      NetClientOptionsKind_lookup[opts->kind]);

            return -1;

        }

    }

    return 0;

}

finally we found function pointers, here are functions for that pointer structure.

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,

};


Posted by MeatNBrew
l

usb initialization is starting in vl.c, line 4259. as below.

if (usb_enabled(false)) {
if (foreach_device_config(DEV_USB, usb_parse) < 0)
exit(1);
}

static int usb_parse(const char *cmdline)
{
int r;
r = usb_device_add(cmdline);
if (r < 0) {
fprintf(stderr, "qemu: could not add USB device '%s'\n", cmdline);
}
return r;
}

static int usb_device_add(const char *devname)
{
USBDevice *dev = NULL;
#ifndef CONFIG_LINUX
const char *p;
#endif

if (!usb_enabled(false)) {
return -1;
}

/* drivers with .usbdevice_name entry in USBDeviceInfo */
dev = usbdevice_create(devname);
if (dev)
goto done;

/* the other ones */
#ifndef CONFIG_LINUX
/* only the linux version is qdev-ified, usb-bsd still needs this */
if (strstart(devname, "host:", &p)) {
dev = usb_host_device_open(usb_bus_find(-1), p);
}
#endif
if (!dev)
return -1;

done:
return 0;
}

USBDevice *usbdevice_create(const char *cmdline)
{
USBBus *bus = usb_bus_find(-1 /* any */);
LegacyUSBFactory *f = NULL;
GSList *i;
char driver[32];
const char *params;
int len;

params = strchr(cmdline,':');
if (params) {
params++;
len = params - cmdline;
if (len > sizeof(driver))
len = sizeof(driver);
pstrcpy(driver, len, cmdline);
} else {
params = "";
pstrcpy(driver, sizeof(driver), cmdline);
}

for (i = legacy_usb_factory; i; i = i->next) {
f = i->data;
if (strcmp(f->usbdevice_name, driver) == 0) {
break;
}
}
if (i == NULL) {
#if 0
/* no error because some drivers are not converted (yet) */
error_report("usbdevice %s not found", driver);
#endif
return NULL;
}

if (!bus) {
error_report("Error: no usb bus to attach usbdevice %s, "
"please try -machine usb=on and check that "
"the machine model supports USB", driver);
return NULL;
}

if (!f->usbdevice_init) {
if (*params) {
error_report("usbdevice %s accepts no params", driver);
return NULL;
}
return usb_create_simple(bus, f->name);
}
return f->usbdevice_init(bus, params);
}

if it uses classic device, usb_create_simple will be called for usb_device_type_info.

And,  if it doesn't, we should find what is assinged to usbdevice_init function pointer.

classic initialization for USB is as below.

static void usb_device_class_init(ObjectClass *klass, void *data)
{
DeviceClass *k = DEVICE_CLASS(klass);
k->bus_type = TYPE_USB_BUS;
k->init = usb_qdev_init;
k->unplug = qdev_simple_unplug_cb;
k->exit = usb_qdev_exit;
k->props = usb_props;
}

static const TypeInfo usb_device_type_info = {
.name = TYPE_USB_DEVICE,
.parent = TYPE_DEVICE,
.instance_size = sizeof(USBDevice),
.abstract = true,
.class_size = sizeof(USBDeviceClass),
.class_init = usb_device_class_init,
};

and here are a function initialization for USB. every usb function uses this usb_legacy_register function to initialize.

void usb_legacy_register(const char *typename, const char *usbdevice_name,
USBDevice *(*usbdevice_init)(USBBus *bus,
const char *params))
{
if (usbdevice_name) {
LegacyUSBFactory *f = g_malloc0(sizeof(*f));
f->name = typename;
f->usbdevice_name = usbdevice_name;
f->usbdevice_init = usbdevice_init;
legacy_usb_factory = g_slist_append(legacy_usb_factory, f);
}
}

but, usbdevice_init function is also a function pointer. so i listed up functions using usb_legacy_register function.

ccid_register_type, usb_audio_register_type, usb_bt_register_type, usb_hid_register_type, usb_msd_register_type, usb_host_register_type, usb_net_register_type, usb_serial_register_type, usb_wacom_register_type.

 

The list up above, those are USBDeviceClass, not DeviceClass.  both have init function, so the function DeviceClass has will be called, and then, the functions USBDeviceClass will be called by DeviceClass's init function.

 

and finally, all those functions are going to call qdev_init_nofail function.

as we know, this function call  object_property_set_bool(OBJECT(dev), true, "realized", &local_err); for initializing device.

then, props->set is going to be called and props and a init function is assign in classes for USB. 

DeviceState calls device_set_realized, and DeviceClass calls deivce_realize. and then, DeviceClass calls init function.

Posted by MeatNBrew
l

link between IDE PortIO and Drive Operation

2014. 5. 23. 22:33 by MeatNBrew

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

Process of Device Initializing in QEMU

2014. 5. 23. 22:32 by MeatNBrew

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

Network Card Initialization of QEMU

2014. 5. 23. 22:32 by MeatNBrew

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.







'Virtualization' 카테고리의 다른 글

Process of Device Initializing in QEMU  (0) 2014.05.23
Network Card Initialization of QEMU  (0) 2014.05.23
Module Starting Points  (0) 2014.05.23
Qemu's Process of Reading or Writing Files  (0) 2014.05.23
IDE initializing Process  (0) 2014.05.23
Posted by MeatNBrew
l

Module Starting Points

2014. 5. 23. 22:29 by MeatNBrew

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

Qemu's Process of Reading or Writing Files

2014. 5. 23. 22:28 by MeatNBrew

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.