vnet.c revision 7b1f684a14f99a2b9b1b2561f484ff648eff6d9b
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/ethernet.h>
#include <sys/mac_ether.h>
#include <sys/vnet_mailbox.h>
#include <sys/vnet_common.h>
/*
* Function prototypes.
*/
/* DDI entrypoints */
/* MAC entrypoints */
static int vnet_m_start(void *);
static void vnet_m_stop(void *);
static int vnet_m_promisc(void *, boolean_t);
static int vnet_m_unicst(void *, const uint8_t *);
/* vnet internal functions */
static int vnet_mac_register(vnet_t *);
/* Forwarding database (FDB) routines */
static void vnet_res_start_task(void *arg);
static void vnet_res_start_task(void *arg);
/* Exported to to vnet_dds */
/* Externs that are imported from vnet_gen */
extern int vgen_uninit(void *arg);
/* Externs that are imported from vnet_dds */
extern void vdds_mod_init(void);
extern void vdds_mod_fini(void);
#define VNET_FDBE_REFHOLD(p) \
{ \
atomic_inc_32(&(p)->refcnt); \
}
#define VNET_FDBE_REFRELE(p) \
{ \
atomic_dec_32(&(p)->refcnt); \
}
static mac_callbacks_t vnet_m_callbacks = {
0,
NULL,
NULL,
};
/*
* Linked list of "vnet_t" structures - one per instance.
*/
/* Tunables */
/*
* Set this to non-zero to enable additional internal receive buffer pools
* based on the MTU of the device for better performance at the cost of more
* memory consumption. This is turned off by default, to use allocb(9F) for
* receive buffer allocations of sizes > 2K.
*/
/* # of chains in fdb hash table */
/* Internal tunables */
/*
* Default vlan id. This is only used internally when the "default-vlan-id"
* property is not present in the MD device node. Therefore, this should not be
* used as a tunable; if this value is changed, the corresponding variable
* should be updated to the same value in vsw and also other vnets connected to
* the same vsw.
*/
/* delay in usec to wait for all references on a fdb entry to be dropped */
static struct ether_addr etherbroadcastaddr = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/*
* Property names
*/
static char macaddr_propname[] = "local-mac-address";
/*
* This is the string displayed by modinfo(1m).
*/
static char vnet_ident[] = "vnet driver";
extern struct mod_ops mod_driverops;
static struct cb_ops cb_vnetops = {
nulldev, /* cb_open */
nulldev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
nodev, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
NULL, /* cb_stream */
(int)(D_MP) /* cb_flag */
};
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
NULL, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
vnetattach, /* devo_attach */
vnetdetach, /* devo_detach */
nodev, /* devo_reset */
&cb_vnetops, /* devo_cb_ops */
};
&mod_driverops, /* Type of module. This one is a driver */
vnet_ident, /* ID string */
&vnetops /* driver specific ops */
};
static struct modlinkage modlinkage = {
};
#ifdef DEBUG
/*
* Print debug messages - set to 0xf to enable all msgs
*/
int vnet_dbglevel = 0x8;
static void
{
char buf[512];
} else {
}
}
#endif
/* _init(9E): initialize the loadable module */
int
_init(void)
{
int status;
if (status != 0) {
}
return (status);
}
/* _fini(9E): prepare the module for unloading. */
int
_fini(void)
{
int status;
if (status != 0)
return (status);
return (status);
}
/* _info(9E): return information about the loadable module */
int
{
}
/*
* attach(9E): attach a device to the system.
* called once for each instance of the device on the system.
*/
static int
{
int status;
int instance;
char qname[TASKQ_NAMELEN];
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
case DDI_PM_RESUME:
default:
goto vnet_attach_fail;
}
/* allocate vnet_t and mac_t structures */
if (status != 0) {
goto vnet_attach_fail;
}
/* setup links to vnet_t from both devinfo and mac_t */
/* read the mac address */
if (status != DDI_SUCCESS) {
goto vnet_attach_fail;
}
if (reg == -1) {
goto vnet_attach_fail;
}
TASKQ_DEFAULTPRI, 0)) == NULL) {
instance);
goto vnet_attach_fail;
}
/* add to the list of vnet devices */
vnet_headp = vnetp;
/*
* Initialize the generic vnet plugin which provides
* communication via sun4v LDC (logical domain channel) based
* resources. It will register the LDC resources as and when
* they become available.
*/
if (status != DDI_SUCCESS) {
goto vnet_attach_fail;
}
/* register with MAC layer */
if (status != DDI_SUCCESS) {
goto vnet_attach_fail;
}
return (DDI_SUCCESS);
if (attach_state & AST_vnet_list) {
/* unlink from instance(vnet_t) list */
break;
}
}
}
if (attach_state & AST_vdds_init) {
}
if (attach_state & AST_taskq_create) {
}
if (attach_state & AST_fdbh_alloc) {
}
if (attach_state & AST_vgen_init) {
}
if (attach_state & AST_vnet_alloc) {
}
return (DDI_FAILURE);
}
/*
* detach(9E): detach a device from the system.
*/
static int
{
int instance;
int rv;
goto vnet_detach_fail;
}
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
case DDI_PM_SUSPEND:
default:
goto vnet_detach_fail;
}
(void) vdds_cleanup(vnetp);
if (rv != DDI_SUCCESS) {
goto vnet_detach_fail;
}
/*
* Unregister from the MAC subsystem. This can fail, in
* particular if there are DLPI style-2 streams still open -
* in which case we just return failure.
*/
goto vnet_detach_fail;
/* unlink from instance(vnet_t) list */
break;
}
}
/* destroy fdb */
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
static int
vnet_m_start(void *arg)
{
return (VNET_SUCCESS);
}
static void
vnet_m_stop(void *arg)
{
}
}
/* set the unicast mac address of the device */
static int
{
/*
* NOTE: setting mac address dynamically is not supported.
*/
return (VNET_FAILURE);
}
static int
{
int rv = VNET_SUCCESS;
}
}
return (rv);
}
/* set or clear promiscuous mode on the device */
static int
{
/*
* NOTE: setting promiscuous mode is not supported, just return success.
*/
return (VNET_SUCCESS);
}
/*
* Transmit a chain of packets. This function provides switching functionality
* based on the destination mac address to reach other guests (within ldoms) or
* external hosts.
*/
mblk_t *
{
struct ether_header *ehp;
/*
* Find fdb entry for the destination
* and hold a reference to it.
*/
/*
* Destination found in FDB.
* The destination is a vnet device within ldoms
* and directly reachable, invoke the tx function
* in the fdb entry.
*/
/* tx done; now release ref on fdb entry */
/* m_tx failed */
break;
}
} else {
(IS_MULTICAST(ehp)));
/*
* Destination is not in FDB.
* If the destination is broadcast or multicast,
* then forward the packet to vswitch.
* If a Hybrid resource avilable, then send the
* unicast packet via hybrid resource, otherwise
* forward it to vswitch.
*/
} else {
}
/*
* no fdb entry to vsw? drop the packet.
*/
continue;
}
/* ref hold the fdb entry to vsw */
/* tx done; now release ref on fdb entry */
/* m_tx failed */
break;
}
}
}
return (mp);
}
/* get statistics from the device */
int
{
/*
* get the specified statistic from each transport and return the
* aggregate val. This obviously only works for counters.
*/
return (ENOTSUP);
}
}
return (0);
}
/* wrapper function for mac_register() */
static int
{
int err;
return (DDI_FAILURE);
/*
* Finally, we're ready to register ourselves with the MAC layer
* interface; if this succeeds, we're all ready to start()
*/
}
/* read the mac address of the device */
static int
{
int rv;
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static void
{
char hashname[MAXNAMELEN];
mod_hash_null_valdtor, sizeof (void *));
}
static void
{
/* destroy fdb-hash-table */
vnetp->fdb_nchains = 0;
}
}
/*
* Add an entry into the fdb.
*/
void
{
int rv;
/*
* If the entry being added corresponds to LDC_SERVICE resource,
* that is, vswitch connection, it is added to the hash and also
* the entry is cached, an additional reference count reflects
* this. The HYBRID resource is not added to the hash, but only
* cached, as it is only used for sending out packets for unknown
* unicast destinations.
*/
/*
* Note: duplicate keys will be rejected by mod_hash.
*/
if (rv != 0) {
return;
}
}
/* Cache the fdb entry to vsw-port */
/* Cache the fdb entry to hybrid resource */
}
}
/*
* Remove an entry from fdb.
*/
static void
{
int rv;
/*
* Remove the entry from fdb hash table.
* This prevents further references to this fdb entry.
*/
(mod_hash_val_t *)&tmp);
if (rv != 0) {
/*
* As the resources are added to the hash only
* after they are started, this can occur if
* a resource unregisters before it is ever started.
*/
return;
}
}
}
/*
* If there are threads already ref holding before the entry was
* removed from hash table, then wait for ref count to drop to zero.
*/
}
}
/*
* Search fdb for a given mac address. If an entry is found, hold
* a reference to it and return the entry; else returns NULL.
*/
static vnet_res_t *
{
int rv;
if (rv != 0)
return (NULL);
return (vresp);
}
/*
* Callback function provided to mod_hash_find_cb(). After finding the fdb
* entry corresponding to the key (macaddr), this callback will be invoked by
* mod_hash_find_cb() to atomically increment the reference count on the fdb
* entry before returning the found entry.
*/
static void
{
}
static void
{
} else {
}
}
void
{
}
}
/*
* Update the new mtu of vnet into the mac layer. First check if the device has
* been plumbed and if so fail the mtu update. Returns 0 on success.
*/
int
{
int rv;
return (EINVAL);
}
"update as the device is plumbed\n",
return (EBUSY);
}
/* update mtu in the mac layer */
if (rv != 0) {
"!vnet%d: Unable to update mtu with mac layer\n",
return (EIO);
}
return (0);
}
/*
* vio_net_resource_reg -- An interface called to register a resource
* with vnet.
* macp -- a GLDv3 mac_register that has all the details of
* a resource and its callbacks etc.
* type -- resource type.
* local_macaddr -- resource's MAC address. This is used to
* associate a resource with a corresponding vnet.
* remote_macaddr -- remote side MAC address. This is ignored for
* the Hybrid resources.
* vhp -- A handle returned to the caller.
* vcb -- A set of callbacks provided to the callers.
*/
{
vnetp = vnet_headp;
break;
}
}
return (ENXIO);
}
/* Dispatch a task to start resources */
return (0);
}
/*
* vio_net_resource_unreg -- An interface to unregister a resource.
*/
void
{
} else {
break;
}
}
}
}
/*
* vnet_dds_rx -- an interface called by vgen to DDS messages.
*/
void
{
}
/*
* vnet_send_dds_msg -- An interface provided to DDS to send
* DDS messages. This simply sends meessages via vgen.
*/
int
{
int rv;
}
return (rv);
}
/*
* vnet_handle_res_err -- A callback function called by a resource
* to report an error. For example, vgen can call to report
* Hybrid resource.
*/
/* ARGSUSED */
static void
{
return;
}
return;
}
}
/*
* vnet_dispatch_res_task -- A function to dispatch tasks start resources.
*/
static void
{
int rv;
vnetp, DDI_NOSLEEP);
if (rv != DDI_SUCCESS) {
"vnet%d:Can't dispatch start resource task",
}
}
}
/*
* vnet_res_start_task -- A taskq callback function that starts a resource.
*/
static void
vnet_res_start_task(void *arg)
{
}
}
/*
* vnet_start_resources -- starts all resources associated with
* a vnet.
*/
static void
{
int rv;
/* skip if it is already started */
continue;
}
if (rv == 0) {
/*
* Successfully started the resource, so now
* add it to the fdb.
*/
}
}
}
/*
* vnet_stop_resources -- stop all resources associated with a vnet.
*/
static void
{
}
}
}