Revision 0b250250b7e7298e0001faf76015da4b03b6b1b3 authored by Prasad Singamsetty on 14 November 2017, 23:13:50 UTC, committed by Michael Roth on 21 June 2018, 01:45:05 UTC
The current implementation of Intel IOMMU code only supports 39 bits
iova address width. This patch provides a new parameter (x-aw-bits)
for intel-iommu to extend its address width to 48 bits but keeping the
default the same (39 bits). The reason for not changing the default
is to avoid potential compatibility problems with live migration of
intel-iommu enabled QEMU guest. The only valid values for 'x-aw-bits'
parameter are 39 and 48.

After enabling larger address width (48), we should be able to map
larger iova addresses in the guest. For example, a QEMU guest that
is configured with large memory ( >=1TB ). To check whether 48 bits
aw is enabled, we can grep in the guest dmesg output with line:
"DMAR: Host address width 48".

Signed-off-by: Prasad Singamsetty <prasad.singamsety@oracle.com>
Reviewed-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 37f51384ae05bd50f83308339dbffa3e78404874)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
1 parent d45364e
Raw File
hub.c
/*
 * Hub net client
 *
 * Copyright IBM, Corp. 2012
 *
 * Authors:
 *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com>
 *  Zhi Yong Wu       <wuzhy@linux.vnet.ibm.com>
 *
 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
 * See the COPYING.LIB file in the top-level directory.
 *
 */

#include "qemu/osdep.h"
#include "monitor/monitor.h"
#include "net/net.h"
#include "clients.h"
#include "hub.h"
#include "qemu/iov.h"
#include "qemu/error-report.h"

/*
 * A hub broadcasts incoming packets to all its ports except the source port.
 * Hubs can be used to provide independent network segments, also confusingly
 * named the QEMU 'vlan' feature.
 */

typedef struct NetHub NetHub;

typedef struct NetHubPort {
    NetClientState nc;
    QLIST_ENTRY(NetHubPort) next;
    NetHub *hub;
    int id;
} NetHubPort;

struct NetHub {
    int id;
    QLIST_ENTRY(NetHub) next;
    int num_ports;
    QLIST_HEAD(, NetHubPort) ports;
};

static QLIST_HEAD(, NetHub) hubs = QLIST_HEAD_INITIALIZER(&hubs);

static ssize_t net_hub_receive(NetHub *hub, NetHubPort *source_port,
                               const uint8_t *buf, size_t len)
{
    NetHubPort *port;

    QLIST_FOREACH(port, &hub->ports, next) {
        if (port == source_port) {
            continue;
        }

        qemu_send_packet(&port->nc, buf, len);
    }
    return len;
}

static ssize_t net_hub_receive_iov(NetHub *hub, NetHubPort *source_port,
                                   const struct iovec *iov, int iovcnt)
{
    NetHubPort *port;
    ssize_t len = iov_size(iov, iovcnt);

    QLIST_FOREACH(port, &hub->ports, next) {
        if (port == source_port) {
            continue;
        }

        qemu_sendv_packet(&port->nc, iov, iovcnt);
    }
    return len;
}

static NetHub *net_hub_new(int id)
{
    NetHub *hub;

    hub = g_malloc(sizeof(*hub));
    hub->id = id;
    hub->num_ports = 0;
    QLIST_INIT(&hub->ports);

    QLIST_INSERT_HEAD(&hubs, hub, next);

    return hub;
}

static int net_hub_port_can_receive(NetClientState *nc)
{
    NetHubPort *port;
    NetHubPort *src_port = DO_UPCAST(NetHubPort, nc, nc);
    NetHub *hub = src_port->hub;

    QLIST_FOREACH(port, &hub->ports, next) {
        if (port == src_port) {
            continue;
        }

        if (qemu_can_send_packet(&port->nc)) {
            return 1;
        }
    }

    return 0;
}

static ssize_t net_hub_port_receive(NetClientState *nc,
                                    const uint8_t *buf, size_t len)
{
    NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);

    return net_hub_receive(port->hub, port, buf, len);
}

static ssize_t net_hub_port_receive_iov(NetClientState *nc,
                                        const struct iovec *iov, int iovcnt)
{
    NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);

    return net_hub_receive_iov(port->hub, port, iov, iovcnt);
}

static void net_hub_port_cleanup(NetClientState *nc)
{
    NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);

    QLIST_REMOVE(port, next);
}

static NetClientInfo net_hub_port_info = {
    .type = NET_CLIENT_DRIVER_HUBPORT,
    .size = sizeof(NetHubPort),
    .can_receive = net_hub_port_can_receive,
    .receive = net_hub_port_receive,
    .receive_iov = net_hub_port_receive_iov,
    .cleanup = net_hub_port_cleanup,
};

static NetHubPort *net_hub_port_new(NetHub *hub, const char *name)
{
    NetClientState *nc;
    NetHubPort *port;
    int id = hub->num_ports++;
    char default_name[128];

    if (!name) {
        snprintf(default_name, sizeof(default_name),
                 "hub%dport%d", hub->id, id);
        name = default_name;
    }

    nc = qemu_new_net_client(&net_hub_port_info, NULL, "hub", name);
    port = DO_UPCAST(NetHubPort, nc, nc);
    port->id = id;
    port->hub = hub;

    QLIST_INSERT_HEAD(&hub->ports, port, next);

    return port;
}

/**
 * Create a port on a given hub
 * @name: Net client name or NULL for default name.
 *
 * If there is no existing hub with the given id then a new hub is created.
 */
NetClientState *net_hub_add_port(int hub_id, const char *name)
{
    NetHub *hub;
    NetHubPort *port;

    QLIST_FOREACH(hub, &hubs, next) {
        if (hub->id == hub_id) {
            break;
        }
    }

    if (!hub) {
        hub = net_hub_new(hub_id);
    }

    port = net_hub_port_new(hub, name);
    return &port->nc;
}

/**
 * Find a specific client on a hub
 */
NetClientState *net_hub_find_client_by_name(int hub_id, const char *name)
{
    NetHub *hub;
    NetHubPort *port;
    NetClientState *peer;

    QLIST_FOREACH(hub, &hubs, next) {
        if (hub->id == hub_id) {
            QLIST_FOREACH(port, &hub->ports, next) {
                peer = port->nc.peer;

                if (peer && strcmp(peer->name, name) == 0) {
                    return peer;
                }
            }
        }
    }
    return NULL;
}

/**
 * Find a available port on a hub; otherwise create one new port
 */
NetClientState *net_hub_port_find(int hub_id)
{
    NetHub *hub;
    NetHubPort *port;
    NetClientState *nc;

    QLIST_FOREACH(hub, &hubs, next) {
        if (hub->id == hub_id) {
            QLIST_FOREACH(port, &hub->ports, next) {
                nc = port->nc.peer;
                if (!nc) {
                    return &(port->nc);
                }
            }
            break;
        }
    }

    nc = net_hub_add_port(hub_id, NULL);
    return nc;
}

/**
 * Print hub configuration
 */
void net_hub_info(Monitor *mon)
{
    NetHub *hub;
    NetHubPort *port;

    QLIST_FOREACH(hub, &hubs, next) {
        monitor_printf(mon, "hub %d\n", hub->id);
        QLIST_FOREACH(port, &hub->ports, next) {
            monitor_printf(mon, " \\ %s", port->nc.name);
            if (port->nc.peer) {
                monitor_printf(mon, ": ");
                print_net_client(mon, port->nc.peer);
            } else {
                monitor_printf(mon, "\n");
            }
        }
    }
}

/**
 * Get the hub id that a client is connected to
 *
 * @id: Pointer for hub id output, may be NULL
 */
int net_hub_id_for_client(NetClientState *nc, int *id)
{
    NetHubPort *port;

    if (nc->info->type == NET_CLIENT_DRIVER_HUBPORT) {
        port = DO_UPCAST(NetHubPort, nc, nc);
    } else if (nc->peer != NULL && nc->peer->info->type ==
            NET_CLIENT_DRIVER_HUBPORT) {
        port = DO_UPCAST(NetHubPort, nc, nc->peer);
    } else {
        return -ENOENT;
    }

    if (id) {
        *id = port->hub->id;
    }
    return 0;
}

int net_init_hubport(const Netdev *netdev, const char *name,
                     NetClientState *peer, Error **errp)
{
    const NetdevHubPortOptions *hubport;

    assert(netdev->type == NET_CLIENT_DRIVER_HUBPORT);
    assert(!peer);
    hubport = &netdev->u.hubport;

    net_hub_add_port(hubport->hubid, name);
    return 0;
}

/**
 * Warn if hub configurations are likely wrong
 */
void net_hub_check_clients(void)
{
    NetHub *hub;
    NetHubPort *port;
    NetClientState *peer;

    QLIST_FOREACH(hub, &hubs, next) {
        int has_nic = 0, has_host_dev = 0;

        QLIST_FOREACH(port, &hub->ports, next) {
            peer = port->nc.peer;
            if (!peer) {
                warn_report("hub port %s has no peer", port->nc.name);
                continue;
            }

            switch (peer->info->type) {
            case NET_CLIENT_DRIVER_NIC:
                has_nic = 1;
                break;
            case NET_CLIENT_DRIVER_USER:
            case NET_CLIENT_DRIVER_TAP:
            case NET_CLIENT_DRIVER_SOCKET:
            case NET_CLIENT_DRIVER_VDE:
            case NET_CLIENT_DRIVER_VHOST_USER:
                has_host_dev = 1;
                break;
            default:
                break;
            }
        }
        if (has_host_dev && !has_nic) {
            warn_report("vlan %d with no nics", hub->id);
        }
        if (has_nic && !has_host_dev) {
            warn_report("vlan %d is not connected to host network", hub->id);
        }
    }
}

bool net_hub_flush(NetClientState *nc)
{
    NetHubPort *port;
    NetHubPort *source_port = DO_UPCAST(NetHubPort, nc, nc);
    int ret = 0;

    QLIST_FOREACH(port, &source_port->hub->ports, next) {
        if (port != source_port) {
            ret += qemu_net_queue_flush(port->nc.incoming_queue);
        }
    }
    return ret ? true : false;
}
back to top