vnet_dds.c revision 4df55fde49134f9735f84011f23a767c75e393c7
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/prom_plat.h>
#include <sys/ndi_impldefs.h>
#include <sys/ddi_impldefs.h>
#include <sys/ethernet.h>
#include <sys/machsystm.h>
#include <sys/hypervisor_api.h>
#include <sys/mach_descrip.h>
#include <sys/vnet_mailbox.h>
#include <sys/vnet_common.h>
#define VDDS_INO_RANGE_START(x) (x * VDDS_MAX_VRINTRS)
#define HVCOOKIE(c) ((c) & 0xFFFFFFFFF)
#define NIUCFGHDL(c) ((c) >> 32)
/* For "ranges" property */
typedef struct vdds_ranges {
/* For "reg" property */
typedef struct vdds_reg {
} vdds_reg_t;
/* For ddi callback argument */
typedef struct vdds_cb_arg {
/* Functions exported to other files */
void vdds_mod_init(void);
void vdds_mod_fini(void);
void vdds_cleanup_hybrid_res(void *arg);
/* DDS message processing related functions */
static void vdds_process_dds_msg_task(void *arg);
/* Functions imported from other files */
/* HV functions that are used in this file */
#ifdef DEBUG
extern int vnet_dbglevel;
static void
{
char buf[512];
} else {
}
}
#endif
/*
*
* The list of HV versions that support NIU HybridIO. Note,
* the order is higher version to a lower version, as the
* registration is attempted in this order.
*/
static hsvc_info_t niu_hsvc[] = {
};
/*
* Index that points to the successful HV version that
* is registered.
*/
static int niu_hsvc_index = -1;
/*
* Lock to serialize the NIU device node related operations.
*/
/*
* vdds_mod_init -- one time initialization.
*/
void
vdds_mod_init(void)
{
int i;
int rv;
/*
* Try register one by one from niu_hsvc.
*/
for (i = 0; i < (sizeof (niu_hsvc) / sizeof (hsvc_info_t)); i++) {
if (rv == 0) {
niu_hsvc_index = i;
break;
} else {
(void) hsvc_unregister(&niu_hsvc[i]);
}
}
}
minor);
}
/*
* vdds_mod_fini -- one time cleanup.
*/
void
vdds_mod_fini(void)
{
if (niu_hsvc_index != -1) {
}
}
/*
* vdds_init -- vnet instance related DDS related initialization.
*/
int
{
char qname[TASKQ_NAMELEN];
TASKQ_DEFAULTPRI, 0)) == NULL) {
return (ENOMEM);
}
return (0);
}
/*
* vdds_cleanup -- vnet instance related cleanup.
*/
void
{
/* taskq_destroy will wait for all taskqs to complete */
}
/*
* vdds_cleanup_hybrid_res -- Cleanup Hybrid resource.
*/
void
vdds_cleanup_hybrid_res(void *arg)
{
/*
* Task for ADD_SHARE is pending, simply
* cleanup the flags, the task will quit without
* any changes.
*/
vdds->task_flags = 0;
/*
* There is no task pending and a hybrid device
* is present, so dispatch a task to release the share.
*/
}
/*
* Other possible cases include either DEL_SHARE or
* REL_SHARE as pending. In that case, there is nothing
* to do as a task is already pending to do the cleanup.
*/
}
/*
* vdds_cleanup_hio -- An interface to cleanup the hio resources before
* resetting the vswitch port.
*/
void
{
/* Wait for any pending vdds tasks to complete */
/* Wait for the cleanup task to complete */
}
/*
* vdds_process_dds_msg -- Process a DDS message.
*/
void
{
int rv;
return;
}
switch (dmsg->dds_subclass) {
case DDS_VNET_ADD_SHARE:
/*
* Either a task is already pending or
* a hybrid device already exists.
*/
return;
}
if (rv != 0) {
/* Send NACK */
vdds->task_flags = 0;
}
break;
case DDS_VNET_DEL_SHARE:
/*
* ADD_SHARE task still pending, simply clear
* task falgs and ACK.
*/
vdds->task_flags = 0;
return;
}
/* Send NACK */
return;
}
if (rv != 0) {
/* Send NACK */
vdds->task_flags = 0;
}
break;
case DDS_VNET_REL_SHARE:
break;
default:
break;
}
}
/*
* vdds_process_dds_msg_task -- Called from a taskq to process the
* DDS message.
*/
static void
vdds_process_dds_msg_task(void *arg)
{
int rv;
switch (vdds->task_flags) {
case VNET_DDS_TASK_ADD_SHARE:
/*
* max-frame-size value need to be set to
* the full ethernet frame size. That is,
* header + payload + checksum.
*/
sizeof (struct ether_vlan_header) + ETHERFCSL;
} else {
if (rv != 0) {
/* failed - cleanup, send failed DDS message */
if (rv == 0) {
/* use DERR to print by default */
" Hybrid node");
} else {
"destroy Hybrid node",
}
vdds->hio_cookie = 0;
(void) vdds_send_dds_resp_msg(vnetp,
} else {
(void) vdds_send_dds_resp_msg(vnetp,
}
/* DERR used only print by default */
}
break;
case VNET_DDS_TASK_DEL_SHARE:
} else {
vdds->hio_cookie);
if (rv == 0) {
/* use DERR to print by default */
" Hybrid node");
} else {
}
/* TODO: send ACK even for failure? */
vdds->hio_cookie = 0;
}
break;
case VNET_DDS_TASK_REL_SHARE:
vdds->hio_cookie);
if (rv == 0) {
"Hybrid node");
} else {
}
/* TODO: failure case */
(void) vdds_send_dds_rel_msg(vnetp);
vdds->hio_cookie = 0;
}
break;
default:
break;
}
vdds->task_flags = 0;
}
/*
* vdds_send_dds_rel_msg -- Send a DDS_REL_SHARE message.
*/
static int
{
int rv;
/* vio_sid filled by the LDC module */
return (rv);
}
/*
* vdds_send_dds_resp_msg -- Send a DDS response message.
*/
static int
{
int rv;
} else {
}
return (rv);
}
/*
* vdds_create_niu_node -- Create NIU Hybrid node. The NIU nexus
* node also created if it doesn't exist already.
*/
{
if (vdds_hv_hio_capable == B_FALSE) {
return (NULL);
}
/* Check if the nexus node exists already */
/*
* NIU nexus node not found, so create it now.
*/
return (NULL);
}
}
/* Check if NIU node exists already before creating one */
/*
* Hold the niu_dip to prevent it from
* detaching.
*/
} else {
}
} else {
niu_dip);
}
if (e_ddi_branch_held(nexus_dip))
return (niu_dip);
}
/*
* vdds_destroy_niu_node -- Destroy the NIU node.
*/
int
{
int rv;
if (!e_ddi_branch_held(niu_dip))
/*
* As we are destroying now, release the
* hold that was done in during the creation.
*/
if (rv != 0) {
niu_dip);
}
goto dest_exit;
}
/*
* Cleanup the parent's ranges property set
* for this Hybrid device.
*/
return (rv);
}
/*
* vdds_match_niu_nexus -- callback function to verify a node is the
* NIU nexus node.
*/
static int
{
char *name;
int rv;
if (dip == ddi_root_node()) {
return (DDI_WALK_CONTINUE);
}
return (DDI_WALK_CONTINUE);
}
if (rv != DDI_PROP_SUCCESS) {
return (DDI_WALK_CONTINUE);
}
/* Hold before returning */
if (!e_ddi_branch_held(dip))
return (DDI_WALK_TERMINATE);
}
return (DDI_WALK_CONTINUE);
}
/*
* vdds_match_niu_node -- callback function to verify a node is the
* NIU Hybrid node.
*/
static int
{
char *name;
int rv;
return (DDI_WALK_CONTINUE);
}
if (rv != DDI_PROP_SUCCESS) {
return (DDI_WALK_CONTINUE);
}
if (!e_ddi_branch_held(dip))
return (DDI_WALK_TERMINATE);
}
return (DDI_WALK_CONTINUE);
}
/*
* vdds_new_nexus_node -- callback function to set all the properties
* a new NIU nexus node.
*/
static int
{
char *compat[] = { "SUNW,niumx" };
int n;
/* create "niu" property */
DDI_SUCCESS) {
return (DDI_WALK_ERROR);
}
/* create "compatible" property */
dip);
return (DDI_WALK_ERROR);
}
/* create "device_type" property */
dip);
return (DDI_WALK_ERROR);
}
/*
* create "reg" property. The first 28 bits of
* 'addr_hi' are NIU cfg_handle, the 0xc in 28-31 bits
* indicates non-cacheable config.
*/
return (DDI_WALK_ERROR);
}
/*
* Create VDDS_MAX_RANGES so that they are already in place
* before the children are created. While creating the child
* we just modify one of this ranges entries.
*/
for (n = 0; n < nranges; n++) {
}
return (DDI_WALK_ERROR);
}
/* create "#size-cells" property */
dip);
return (DDI_WALK_ERROR);
}
/* create "#address-cells" property */
dip);
return (DDI_WALK_ERROR);
}
return (DDI_WALK_TERMINATE);
}
/*
* vdds_new_niu_node -- callback function to create a new NIU Hybrid node.
*/
static int
{
char *compat[] = { "SUNW,niusl" };
int interrupts[VDDS_MAX_VRINTRS];
int prnglen;
int nintr = 0;
int nrng;
int rnum;
int rv;
return (DDI_WALK_ERROR);
}
/* create "network" property */
DDI_SUCCESS) {
return (DDI_WALK_ERROR);
}
/*
* create "niutype" property, it is set to n2niu to
* indicate NIU Hybrid node.
*/
"n2niu") != DDI_SUCCESS) {
dip);
return (DDI_WALK_ERROR);
}
/* create "compatible" property */
dip);
return (DDI_WALK_ERROR);
}
/* create "device_type" property */
dip);
return (DDI_WALK_ERROR);
}
/* create "reg" property */
return (DDI_WALK_ERROR);
}
return (DDI_WALK_ERROR);
}
/*
* Modify the parent's ranges property to map the "reg" property
* of the new child.
*/
"Failed to get parent's ranges property(pdip=0x%p) rv=%d",
return (DDI_WALK_ERROR);
}
/*
* First scan all ranges to see if a range corresponding
* to this virtual NIU exists already.
*/
break;
}
}
/* Now to try to find an empty range */
break;
}
}
}
return (DDI_WALK_ERROR);
}
/*
* child_hi will have HV cookie as HV cookie is more like
* a port in the HybridIO.
*/
pdip);
return (DDI_WALK_ERROR);
}
/*
* create "local-mac-address" property, this will be same as
* the vnet's mac-address.
*/
dip);
return (DDI_WALK_ERROR);
}
if (rv != 0) {
return (DDI_WALK_ERROR);
}
/* create "interrupts" property */
dip);
return (DDI_WALK_ERROR);
}
/* create "max_frame_size" property */
dip);
return (DDI_WALK_ERROR);
}
return (DDI_WALK_TERMINATE);
}
/*
* vdds_find_node -- A common function to find a NIU nexus or NIU node.
*/
static dev_info_t *
{
int circ;
}
}
}
/*
*/
static dev_info_t *
{
int rv;
pdip = ddi_root_node();
}
return (NULL);
}
}
/*
* vdds_get_interrupts -- A function that binds ino's to channels and
* then provides them to create interrupts property.
*/
static int
{
int rv;
uint64_t i;
*nintr = 0;
return (EIO);
}
return (EIO);
}
/* Check if the number of total channels to be more than 8 */
for (i = 0; i < 4; i++) {
"hvcookie=0x%X vch_idx=0x%lx rv=%d\n",
return (EIO);
}
"hvcookie=0x%X RX vch_idx=0x%lx ino=0x%X\n",
ino++;
} else {
}
intrs++;
*nintr += 1;
}
for (i = 0; i < 4; i++) {
"hvcookie=0x%X vch_idx=0x%lx rv=%d\n",
return (EIO);
}
ino++;
} else {
}
intrs++;
*nintr += 1;
}
return (0);
}
/*
* vdds_release_range_prop -- cleanups an entry in the ranges property
* corresponding to a cookie.
*/
static void
{
int prnglen;
int nrng;
int rnum;
int rv;
"Failed to get nexus ranges property(dip=0x%p) rv=%d",
return;
}
break;
}
}
if (success) {
"Failed to update nexus ranges prop(dip=0x%p)",
}
}
}