Add a hardware implementation template

Set of modifications and additions to establish a platform
for implementing a hardware table in userspace. Modules in
skeleton directory is used as a template for creating
hardware dependent libraries.
This commit is contained in:
Tatsuya Yabe authored and Glen Gibb committed 2010-05-03 15:54:37 -07:00
1 parent b70d37144f
commit 6778bd8cdf
26 files changed
+3133 -40

No files matched your search

+2 -1
View File
@@ -13,7 +13,7 @@ else
distcheck-hook: distcheck-hook:
endif endif
AM_CPPFLAGS = $(SSL_CFLAGS) AM_CPPFLAGS = $(SSL_CFLAGS) -g
AM_CPPFLAGS += -I $(top_srcdir)/include AM_CPPFLAGS += -I $(top_srcdir)/include
AM_CPPFLAGS += -I $(top_srcdir)/lib AM_CPPFLAGS += -I $(top_srcdir)/lib
@@ -65,3 +65,4 @@ include tests/automake.mk
include include/automake.mk include include/automake.mk
include third-party/automake.mk include third-party/automake.mk
include debian/automake.mk include debian/automake.mk
include hw-lib/automake.mk
+88
View File
@@ -169,6 +169,94 @@ AC_DEFUN([OFP_CHECK_HWTABLES],
done done
AC_SUBST(HW_TABLES)]) AC_SUBST(HW_TABLES)])
dnl Checks for --enable-hw-lib and substitutes BUILD_HW_LIBS and plat name
AC_DEFUN([OFP_CHECK_HWLIBS],
[AC_ARG_ENABLE(
[hw-lib],
[AC_HELP_STRING([--enable-hw-lib=PLATFORM],
[Configure and build the specified externally supplied
hardware library: lb4g, t2ref, scorref or nf2])])
case "${enable_hw_lib}" in # (
yes)
AC_MSG_ERROR([--enable-hw-lib has a required argument])
;; # (
''|no)
hw_lib=
NF2=no
LB4G=no
T2REF=no
SCORREF=no
BUILD_HW_LIBS=no
;; # (
nf2)
NF2=yes
LB4G=no
T2REF=no
SCORREF=no
hw_lib=$enable_hw_lib
BUILD_HW_LIBS=yes
;; # (
lb4g)
NF2=no
LB4G=yes
T2REF=no
SCORREF=no
hw_lib=$enable_hw_lib
BUILD_HW_LIBS=yes
;; # (
t2ref)
NF2=no
LB4G=no
T2REF=yes
SCORREF=no
hw_lib=$enable_hw_lib
BUILD_HW_LIBS=yes
;; # (
scorref)
NF2=no
LB4G=no
SCORREF=yes
T2REF=no
hw_lib=$enable_hw_lib
BUILD_HW_LIBS=yes
;; # (
*)
AC_MSG_ERROR([--enable-hw-lib: Unknown platform: ${enable_hw_lib}])
BUILD_HW_LIBS=no
;;
esac
if test $BUILD_HW_LIBS = yes; then
if test -e "$srcdir/hw-lib/automake.mk"; then
:
else
AC_MSG_ERROR([cannot configure HW libraries without "hw-lib" directory])
fi
AC_DEFINE([BUILD_HW_LIBS], [1],
[Whether the OpenFlow hardware libraries are available])
fi
if test $NF2 = yes; then
AC_DEFINE([NF2], [1],
[Support NetFPGA platform])
fi
if test $LB4G = yes; then
AC_DEFINE([LB4G], [1],
[Support Stanford-LB4G platform])
fi
if test $T2REF = yes; then
AC_DEFINE([T2REF], [1],
[Support Broadcom 56634 reference platform])
fi
if test $SCORREF = yes; then
AC_DEFINE([SCORREF], [1],
[Support Broadcom 56820 reference platform])
fi
AM_CONDITIONAL([NF2], [test $NF2 = yes])
AM_CONDITIONAL([LB4G], [test $LB4G = yes])
AM_CONDITIONAL([T2REF], [test $T2REF = yes])
AM_CONDITIONAL([SCORREF], [test $SCORREF = yes])
AM_CONDITIONAL([BUILD_HW_LIBS], [test $BUILD_HW_LIBS = yes])
AC_SUBST(HW_LIB)])
dnl Checks for net/if_packet.h. dnl Checks for net/if_packet.h.
AC_DEFUN([OFP_CHECK_IF_PACKET], AC_DEFUN([OFP_CHECK_IF_PACKET],
[AC_CHECK_HEADER([net/if_packet.h], [AC_CHECK_HEADER([net/if_packet.h],
+1
View File
@@ -54,6 +54,7 @@ fi
OFP_CHECK_LIBOPENFLOW OFP_CHECK_LIBOPENFLOW
OFP_CHECK_IF_PACKET OFP_CHECK_IF_PACKET
OFP_CHECK_HWTABLES OFP_CHECK_HWTABLES
OFP_CHECK_HWLIBS
AC_SYS_LARGEFILE AC_SYS_LARGEFILE
AC_CHECK_FUNCS([strsignal]) AC_CHECK_FUNCS([strsignal])
+30
View File
@@ -0,0 +1,30 @@
#
# Hardware library dependency file definitions.
#
# Sample name
if LB4G
#
# Sample library
#
noinst_LIBRARIES += hw-lib/libskeleton.a
hw_lib_libskeleton_a_SOURCES = \
hw-lib/skeleton/of_hw_platform.h \
hw-lib/skeleton/txrx.c \
hw-lib/skeleton/txrx.h \
hw-lib/skeleton/hw_drv.c \
hw-lib/skeleton/hw_drv.h \
hw-lib/skeleton/hw_flow.c \
hw-lib/skeleton/hw_flow.h \
hw-lib/skeleton/port.c \
hw-lib/skeleton/port.h \
hw-lib/skeleton/debug.h
hw_lib_libskeleton_a_CPPFLAGS = $(AM_CPPFLAGS) $(OF_CPP_FLAGS)
hw_lib_libskeleton_a_CPPFLAGS += -I hw-lib/skeleton
hw_lib_libskeleton_a_CPPFLAGS += -I $(HW_SYSTEM)/include
hw_lib_libskeleton_a_CPPFLAGS += -DSAMPLE_PLAT
hw_lib_libskeleton_a_CPPFLAGS += -DSKELETON
endif
+106
View File
@@ -0,0 +1,106 @@
#ifndef OF_HW_DEBUG_H
#define OF_HW_DEBUG_H 1
#include <stdio.h>
#include <openflow/openflow.h>
#if !defined(HWTABLE_NO_DEBUG)
extern int of_hw_debug;
#define dbg_send(mod, lvl, fmt, args...) \
if (of_hw_debug >= (lvl)) fprintf(stderr, fmt, ##args)
#endif
#define DBG_LVL_NONE -1 /* All output off */
#define DBG_LVL_ERROR 0 /* Default value */
#define DBG_LVL_ALWAYS 0 /* For requested dump output */
#define DBG_LVL_WARN 1
#define DBG_LVL_VERBOSE 2
#define DBG_LVL_VVERB 3 /* Include success indications */
/* Sorry for the lazy syntax here. */
#define DBG_CHECK(lvl) (((lvl) >= 0) && (of_hw_debug >= (lvl)))
#define DBG_ERROR(fmt, args...) dbg_send(0, DBG_LVL_ERROR, fmt, ##args)
#define DBG_ALWAYS(fmt, args...) dbg_send(0, DBG_LVL_ALWAYS, fmt, ##args)
#define DBG_WARN(fmt, args...) dbg_send(0, DBG_LVL_WARN, fmt, ##args)
#define DBG_VERBOSE(fmt, args...) dbg_send(0, DBG_LVL_VERBOSE, fmt, ##args)
#define DBG_VVERB(fmt, args...) dbg_send(0, DBG_LVL_VVERB, fmt, ##args)
#define DBG_NONE(fmt, args...)
/* Same as DEBUG_ALWAYS */
#define DEBUGK(fmt, args...) dbg_send(0, DBG_LVL_ALWAYS, fmt, ##args)
#define REPORT_ERROR(str) \
DBG_ERROR("ERROR: %s:%d. %s\n", __FUNCTION__, __LINE__, str)
/* Default debugging location string */
#define ANNOUNCE_LOCATION DBG_VVERB("%s: %d\n", __FUNCTION__, __LINE__)
#define DBG_INCR(cnt) (++(cnt))
/* Should assert return? Not presently */
#define ASSERT(cond) \
if (!(cond)) DBG_ERROR("ASSERTION %s IN %s FAILED LINE %d\n", \
#cond, __FUNCTION__, __LINE__)
/* DEBUG for HW flow lists */
#define HW_FLOW_MAGIC 0xba5eba11
#define HW_FLOW_MAKE_VALID(hf) (hf)->magic = HW_FLOW_MAGIC
#define HW_FLOW_IS_VALID(hf) \
(((hf) != NULL) && ((hf)->magic == HW_FLOW_MAGIC))
/*
* Carry out an operation and check for error, optionally returning
* from the calling routine. To avoid compiler
* warnings in routines with void return, we give two macros.
*
* WARNING: This check uses rv != 0 (not just rv < 0).
*/
#define TRY(op, str) do { \
int rv; \
if (((rv = (op)) < 0)) { \
DBG_ERROR("ERROR %d: %s\n", rv, str); \
return rv; \
} else { \
DBG_NONE("%s: success\n", str); \
} \
} while (0)
#define TRY_NR(op, str) do { \
int rv; \
if (((rv = (op)) != 0)) { \
DBG_ERROR("ERROR %d: %s\n", rv, str); \
} else { \
DBG_NONE("%s: success\n", str); \
} \
} while (0)
#else /* No debugging */
#define DBG_CHECK(lvl) 0
#define ANNOUNCE_LOCATION
#define ACT_STRING(action) ""
#define DBG_INCR(cnt)
#define DBG_ERROR(fmt, args...)
#define DBG_ALWAYS(fmt, args...)
#define DBG_WARN(fmt, args...)
#define DBG_VERBOSE(fmt, args...)
#define DBG_VVERB(fmt, args...)
#define DBG_NONE(fmt, args...)
#define DEBUGK(fmt, args...)
#define REPORT_ERROR(str)
#define ASSERT(cond)
#define HW_FLOW_MAKE_VALID(hf)
#define HW_FLOW_IS_VALID(hf) 1
#define TRY(op, str) do { \
int rv; \
if (((rv = (op)) != 0)) return rv; \
} while (0)
#define TRY_NR(op, str) (void)op
#endif /* !defined(HWTABLE_NO_DEBUG) */
#endif /* OF_HW_DEBUG_H */
+309
View File
@@ -0,0 +1,309 @@
/*
* Skeleton hardware datapath, internal implemenation
*
*/
#include <of_hw_api.h>
#include <string.h>
#include <stdlib.h>
#include "list.h"
#include "os.h"
#include "debug.h"
#include "of_hw_platform.h"
#include "hw_drv.h"
#include "hw_flow.h"
#include "port.h"
#include "txrx.h"
#include "udatapath/switch-flow.h"
/****************************************************************
*
* Hardware resource management section
*
****************************************************************/
static int global_init_done = 0;
int of_hw_drv_instances = 0;
#if !defined(HWTABLE_NO_DEBUG)
int of_hw_debug = DBG_LVL_WARN;
#endif
/* Internal initialization of HW datapath structures */
static int
hw_drv_global_init(int init_hw)
{
HW_DRV_MUTEX_INIT;
of_hw_ports_init();
if (init_hw) { /* Do hardware initialization */
TRY(PLATFORM_INIT(), "PLATFORM INIT");
TRY_NR(of_hw_fp_setup(), "global FP setup");
}
global_init_done = 1;
return 0;
}
/* Skeleton capabilities structure */
static of_hw_driver_caps_t of_hw_caps = {
.flags = ,
.max_flows = ,
.wc_supported = OFPFW_ALL,
.actions_supported = OFPAT_XXX,
.ofpc_flags = OFPC_XXX
};
/* Initialize the internal HW datapath structure */
static int
hw_drv_int_init(of_hw_driver_int_t *hw_int, struct datapath *dp)
{
static int dp_idx = 0;
/* Fill in caps object */
memcpy(&hw_int->hw_driver.caps, &of_hw_caps, sizeof(of_hw_caps));
hw_int->dp = dp;
hw_int->dp_idx = dp_idx++;
hw_int->n_flows = 0;
list_init(&hw_int->flows);
list_init(&hw_int->iter_flows);
hw_int->next_serial = 0;
return 0;
}
/****************************************************************
*
* Driver functions
*
****************************************************************/
/*
* of_hw_ioctl
*
* HW IOCTL function
*/
static int
of_hw_ioctl(of_hw_driver_t *hw_drv, uint32_t op, void **io_param,
int *io_len UNUSED)
{
int val;
of_hw_driver_int_t *hw_int;
int rv = 0;
hw_int = (of_hw_driver_int_t *)hw_drv;
switch (op) {
case OF_HW_IOCTL_TABLE_DEBUG_SET:
val = *(int *)(*io_param);
DBG_WARN("Changing table debug value to %d\n", val);
/* FIXME: Use HW driver for debug level? */
hw_int->table_debug_level = val;
of_hw_debug = val;
break;
case OF_HW_IOCTL_PORT_DEBUG_SET:
val = *(int *)(*io_param);
DBG_WARN("Changing port debug value to %d\n", val);
hw_int->port_debug_level = val;
break;
case OF_HW_IOCTL_BYTE_PKT_CNTR_SET:
val = *(int *)(*io_param);
if ((val != OF_HW_CNTR_PACKETS) &&
(val != OF_HW_CNTR_BYTES)) {
DBG_ERROR("Bad byte/pkt counters select value %d\n", val);
} else {
hw_int->stat_sel = val;
}
break;
default:
rv = -1; /* Change to UNSUPPORTED */
break;
}
return 0;
}
static void
of_hw_table_destroy(struct sw_table *table)
{
of_hw_driver_int_t *hw_int;
hw_int = (of_hw_driver_int_t *)table;
if (hw_int != NULL && hw_int->dp != NULL) {
DBG_WARN("Table destroy called for dp idx %d\n",
hw_int->dp_idx);
} else {
DBG_ERROR("Table destroy called on NULL dp\n");
}
delete_of_hw_driver(&hw_int->hw_driver);
}
static int
of_hw_table_iterate(struct sw_table *table,
const struct sw_flow_key *key, uint16_t out_port,
struct sw_table_position *position,
int (*callback)(struct sw_flow *flow, void *private),
void *private)
{
of_hw_driver_int_t *hw_int;
struct sw_flow *flow;
unsigned long start;
hw_int = (of_hw_driver_int_t *)table;
if (hw_int != NULL) {
start = ~position->private[0];
LIST_FOR_EACH (flow, struct sw_flow, iter_node, &hw_int->iter_flows) {
if (flow->serial <= start
&& flow_matches_2wild(key, &flow->key)
&& flow_has_out_port(flow, out_port)) {
int error;
/* Update stats as that's what's generally used */
TRY_NR(of_hw_sw_flow_stat_update(flow, NULL, false),
"sw flow stat update");
error = callback(flow, private);
if (error) {
position->private[0] = ~(flow->serial - 1);
return error;
}
}
}
} else {
DBG_ERROR("Table iterate called on NULL driver or SW flow table\n");
return -1;
}
return 0;
}
static const char *hw_drv_name = "HW FlowDriver";
static void
of_hw_sw_table_stats(struct sw_table *table, struct sw_table_stats *stats)
{
of_hw_driver_int_t *hw_int;
unsigned long matched = 0;
unsigned long missed = 0;
hw_int = (of_hw_driver_int_t *)table;
stats->name = hw_drv_name;
stats->wildcards = OFPFW_ALL;
stats->n_flows = hw_int->n_flows;
stats->max_flows = of_hw_caps.max_flows;
/* FIXME: Collect stats */
TRY_NR(of_hw_table_stats_update(hw_int, &matched, &missed),
"of_hw_table_stats_update");
stats->n_lookup = missed + matched;
stats->n_matched = matched;
}
static int
of_hw_table_stats_get(of_hw_driver_t *hw_drv, struct ofp_table_stats *stats)
{
DBG_WARN("TABLE STATS FIXME\n");
return 0;
}
/*
* Create and initialize a new hardware datapath object
*/
of_hw_driver_t *
new_of_hw_driver(struct datapath *dp)
{
struct sw_table *sw_tab;
of_hw_driver_t *hw_drv;
of_hw_driver_int_t *hw_int;
if (!global_init_done) {
if (hw_drv_global_init(1) < 0) {
REPORT_ERROR("HW driver global init failed\n");
return NULL;
}
}
hw_int = ALLOC(sizeof(*hw_int));
if (hw_int == NULL) {
REPORT_ERROR("Could not allocate HW driver\n");
return NULL;
}
memset(hw_int, 0, sizeof(*hw_int));
if (hw_drv_int_init(hw_int, dp) < 0) {
FREE(hw_int);
return NULL;
}
/* These all point to the same place */
hw_drv = &hw_int->hw_driver;
sw_tab = &hw_drv->sw_table;
sw_tab->n_lookup = 0;
sw_tab->n_matched = 0;
/* Fill out the function pointers */
sw_tab->lookup = of_hw_flow_lookup;
sw_tab->insert = of_hw_flow_install;
sw_tab->modify = of_hw_flow_modify;
sw_tab->delete = of_hw_flow_delete;
sw_tab->timeout = of_hw_flow_timeout;
sw_tab->destroy = of_hw_table_destroy;
sw_tab->iterate = of_hw_table_iterate;
sw_tab->stats = of_hw_sw_table_stats;
hw_drv->table_stats_get =of_hw_table_stats_get;
hw_drv->port_stats_get = of_hw_port_stats_get;
hw_drv->flow_stats_get = NULL;
hw_drv->aggregate_stats_get = NULL;
hw_drv->port_add = of_hw_port_add;
hw_drv->port_remove = of_hw_port_remove;
hw_drv->port_link_get = of_hw_port_link_get;
hw_drv->port_enable_set = of_hw_port_enable_set;
hw_drv->port_enable_get = of_hw_port_enable_get;
hw_drv->port_queue_config = of_hw_port_queue_config;
hw_drv->port_queue_remove = of_hw_port_queue_remove;
hw_drv->port_change_register = of_hw_port_change_register;
hw_drv->packet_send = of_hw_packet_send;
hw_drv->packet_receive_register = of_hw_packet_receive_register;
hw_drv->ioctl = of_hw_ioctl;
++of_hw_drv_instances;
return hw_drv;
}
/*
* Deallocate a hardware datapath object
* If clear_hw is set, the HW structures related to the
* datapath are also cleared.
*
* In general, clear_hw should be set for now.
*/
void
delete_of_hw_driver(of_hw_driver_t *hw_drv)
{
int idx;
of_hw_driver_int_t *hw_int;
hw_int = (of_hw_driver_int_t *)hw_drv;
/* Clear the port table of ownership for this dp */
FOREACH_DP_PORT(idx, hw_drv) {
of_hw_ports[idx].owner = NULL;
}
of_hw_flow_remove_all(hw_int);
FREE(hw_drv);
--of_hw_drv_instances;
}
+97
View File
@@ -0,0 +1,97 @@
#ifndef OF_HW_DRV_H
#define OF_HW_DRV_H 1
#include <of_hw_api.h>
#include "of_hw_platform.h"
#include "list.h"
/****************************************************************
*
* High Level Design Notes
*
* Hardware tables are managed field_control_t and entry_control_t
* structures in hw_flow.h; these roughly mirror the HW structure
* and are preallocated.
*
* The glue between these and SW flows are provided by the HW flow
* objects (also in hw_flow.h) which are dynamically allocated and
* logically extend the SW flow object.
*
* A linear table of HW flow objects is maintained, mostly drawn
* from the existing table-linear.c implementation in udatapath.
*
*/
/****************************************************************
* Hardware independent port mapping macros
****************************************************************/
/* Map ports using software data structures and DP check; independent of HW */
#define OF_PORT_IN_DP(_hwdrv, _i) (((_i) < OF_HW_MAX_PORT) && \
(of_hw_ports[_i].owner == (of_hw_driver_t *)(_hwdrv)))
#define OF_PORT_TO_HW_PORT(_hwdrv, _i) \
(OF_PORT_IN_DP(_hwdrv, _i) ? of_hw_ports[_i].port : -1)
/* TBD: Define flow tracking objects */
/****************************************************************
*
* Hardware datapath internal control structure
* Extends generic hardware datapath structure
* (which currently extends software table structure)
*
****************************************************************/
/* Counts the number of instances */
extern int of_hw_drv_instances;
typedef struct of_hw_driver_int {
of_hw_driver_t hw_driver;
struct datapath *dp; /* Owning datapath */
/* Maintain a linked list of flows for tracking */
struct list flows; /* The main list */
struct list iter_flows; /* For iteration operation */
unsigned long int next_serial;
/* Callback function for received packets. */
of_packet_in_f rx_handler;
void *rx_cookie;
/* Callback function for port change notification */
of_port_change_f port_change;
void *port_change_cookie;
/* Lock object? */
// struct list sw_flows;
// struct list iter_sw_flows;
// struct sw_flow *iter_state; /* Place tracker for interrupted iterations */
/* Stats */
uint32_t n_flows; /* Current number of flows in table */
uint32_t n_inserts; /* Total inserts */
uint32_t rx_pkt_alloc_failures;
uint32_t insert_errors;
/* Configuration */
/* Port bitmap of ports in DP by device */
FIXME port_bitmap;
FIXME flood_bitmap;
int stat_sel; /* Packets or bytes */
int dp_idx; /* In HW dp, but not SW dp */
int table_debug_level;
int port_debug_level;
} of_hw_driver_int_t;
/* HW_DRV_MUTEX object ? */
#define HW_DRV_MUTEX_INIT /* TBD */
#define HW_DRV_LOCK /* TBD */
#define HW_DRV_UNLOCK /* TBD */
#define HW_INT(hw_drv) ((of_hw_driver_int_t *)hw_drv)
#endif /* OF_HW_DRV_H */
+708
View File
@@ -0,0 +1,708 @@
/*
* Functions related to hardware flow processing; this is the glue
* and administration between upper level flow operations and lower
* level field processor operations.
*/
#include <arpa/inet.h>
#include <openflow/openflow.h>
#include <of_hw_api.h>
#include "os.h"
#include "debug.h"
#include "hw_drv.h"
#include "port.h"
#include "hw_flow.h"
#include "udatapath/switch-flow.h"
#include "udatapath/datapath.h"
#include "lib/packets.h"
static field_control_t field_control;
/****************************************************************
*
* SW/HW flow correlation book keeping routines
*
****************************************************************/
/* Allocate and initialize a HW flow control structure. */
static hw_flow_t *
hw_flow_create(struct sw_flow *flow)
{
hw_flow_t *hw_flow;
if ((hw_flow = ALLOC(sizeof(*hw_flow))) != NULL) {
memset(hw_flow, 0, sizeof(*hw_flow));
HW_FLOW_MAKE_VALID(hw_flow);
hw_flow->flow = flow;
flow->private = (void *)hw_flow;
}
return hw_flow;
}
/* Remove an FP entry from a device */
static int
HW_entry_remove(entry_control_t *entry)
{
FIXME;
return 0;
}
/****************************************************************
*
* Flow stats operations
*
****************************************************************/
/*
* Read and convert the stats for an FP entry; maintains state
* of last counter value read and accumulates differences.
*
* Assumes mutex held.
*/
static int
entry_stat_update(entry_control_t *ent, int *used)
{
unsigned long int cur_counter;
FIXME_get_current_stats(cur_counter);
ent->total_counter += cur_counter - ent->last_counter;
ent->last_counter = cur_counter;
if (used != NULL) {
*used |= (ent->used_check != cur_counter);
ent->used_check = cur_counter;
}
return 0;
}
/*
* Get the stats for an OF (SW) flow object from HW tables
* NOTE: This will clear any existing value from the flow
* counters; if SW table counters need to be added in, that
* should be done after this calculation.
*
* If used is non-NULL, it will be filled with a boolean
* indication of whether the flow's counters have changed
* from their current state.
*
* Assumes mutex held.
*
* If force_sync is true, will sync SW counters with HW first
*/
int
of_hw_sw_flow_stat_update(struct sw_flow *flow, int *used, int force_sync)
{
entry_control_t *ent;
hw_flow_t *hw_flow;
int idx;
if (used != NULL) {
*used = false;
}
hw_flow = (hw_flow_t *)(flow->private);
if (!HW_FLOW_IS_VALID(hw_flow)) {
DBG_ERROR("BAD HW FLOW OBJECT %p for flow %p\n", hw_flow, flow);
return -1;
}
ASSERT(hw_flow->entry_count <= HW_FLOWS_PER_FLOW_MAX);
if (force_sync) {
FIXME_sync_hw_stats();
}
/* FIXME: Will this accumulate and clear HW counters? */
hw_flow->hw_byte_count = hw_flow->hw_packet_count = 0;
for (idx = 0; idx < hw_flow->entry_count; idx++) {
ent = hw_flow->entry_list[idx];
if (entry_stat_update(ent, used) < 0) {
DBG_WARN("Warning: could not get stats for flow\n");
continue;
}
FIXME_update proper hw_flow counters;
}
flow->packet_count = hw_flow->hw_packet_count;
flow->byte_count = hw_flow->hw_byte_count;
return 0;
}
/* Read all the stats from the chip and update counters in flow tab */
/* Index 0 holds default rule that indicates a missed count */
/* ASSUMES LOCK HELD */
int
of_hw_table_stats_update(of_hw_driver_int_t *hw_drv, unsigned long *matched,
unsigned long *missed)
{
struct sw_flow *flow, *n;
of_hw_driver_int_t *hw_int;
hw_int = (of_hw_driver_int_t *)hw_drv;
*matched = 0;
*missed = 0;
if (STATS_BYTES(hw_int->stat_sel)) { /* Lookup count not supported */
return -1;
}
LIST_FOR_EACH_SAFE (flow, n, struct sw_flow, node, &hw_int->flows) {
if (flow->private != NULL) {
TRY_NR(of_hw_sw_flow_stat_update(flow, NULL, false),
"update flow stat");
*matched += flow->packet_count;
}
}
/*
* FIXME: To get missed stats, query entry 0 of HW table.
* This isn't really correct if there are entries in the SW datapath
*
* FIXME: If there are multiple data paths, should have one
* default entry qualifying on ports in that datapath with
* another entry dropping any packets on other ports.
*/
{
field_control_t *fc;
entry_control_t *ent;
fc = &field_control;
ent = &fc->entry_list[0];
ASSERT(ent->in_use);
TRY_NR(entry_stat_update(ent, NULL), "tab: entry_stat_update");
*missed += ent->total_counter;
}
return 0;
}
/*
* Remove the FP entry or entries associated to a SW flow from the HW;
*
*/
static int
hw_flow_remove(struct sw_flow *flow)
{
field_control_t *fc;
entry_control_t *ent;
hw_flow_t *hw_flow;
int idx;
#if 0 /* Do not remove entries (debugging) */
return 0;
#endif
if (flow == NULL) {
return 0;
}
hw_flow = (hw_flow_t *)(flow->private);
DBG_VERBOSE("Removing hw flow %p, flow %p\n", hw_flow, flow);
if (!HW_FLOW_IS_VALID(hw_flow)) {
return 0;
}
ASSERT(hw_flow->entry_count <= HW_FLOWS_PER_FLOW_MAX);
TRY_NR(of_hw_sw_flow_stat_update(flow, NULL, true),
"rmv flow stat update");
for (idx = 0; idx < hw_flow->entry_count; idx++) {
ent = hw_flow->entry_list[idx];
ASSERT(ent != NULL);
TRY_NR(HW_entry_remove(ent), "HW_entry_remove");
hw_flow->entry_list[idx] = NULL;
}
FREE(hw_flow);
flow->private = NULL;
return 0;
}
/****************************************************************
*
* Important support routines related to FP setup, actions, flows
*
****************************************************************/
/* ASSERT: of_port belongs to datapath */
static void
add_dport_to_extra(of_hw_driver_int_t *hw_int, int of_port,
hw_flow_extra_t *extra)
{
ASSERT(OF_PORT_IN_DP(hw_int, of_port));
extra->dest_port = MAP_OF_PORT_TO_HW_PORT(hw_int, of_port);
FIXME_update extra->dest_count if appropriate;
}
/* Set up flood/all bitmaps
* NOTE: Assumes source port is not wildcarded.
*/
static void
mcast_bitmaps_set(of_hw_driver_int_t *hw_int, hw_flow_extra_t *extra,
int dest_port)
{
FIXME;
}
/*
* Validate actions for a flow relative to HW capabilities;
*
* Bool: true means supported, false not supported
*
* Accumulate additional info about the flow in extra. Specifically,
* this determines the output ports on each physical device related
* to the flow.
*
* FIXME: No-flood ports are currently not specified
*/
static int
actions_supported_check(of_hw_driver_int_t *hw_int,
const struct sw_flow_key *key,
const struct ofp_action_header *actions,
size_t actions_len,
hw_flow_extra_t *extra)
{
uint8_t *p;
int src_port; /* Port in host order */
int dest_port; /* Port in host order */
int action;
int src_is_wc = false; /* Is source wildcarded? */
ANNOUNCE_LOCATION;
ASSERT(extra != NULL);
FIXME("probably needs work for your platform");
p = (uint8_t *)actions;
src_port = ntohs(key->flow.in_port);
src_is_wc = key->wildcards & OFPFW_IN_PORT;
if (!src_is_wc) {
if (OF_PORT_IN_DP(hw_int, src_port)) {
extra->source_port = MAP_TO_HW_PORT(hw_int, src_port);
} else {
DBG_WARN("Source port %d not in DP %d\n", src_port,
hw_int->dp_idx);
return false;
}
} else {
extra->source_port = -1;
if (of_hw_drv_instances > 1) {
DBG_WARN("Source port WC not supported w/ multi-DP\n");
return false; /* Not currently supported w/ multiple datapaths */
}
/* NOTE/FIXME: Source wildcard with multiple output ports requires
* using a per-port hardware flow rule to allow filtering of
* the source port. (Currently not supported and checked below.)
*/
}
while (actions_len > 0) {
struct ofp_action_header *ah = (struct ofp_action_header *)p;
struct ofp_action_output *oa = (struct ofp_action_output *)p;
size_t len = ntohs(ah->len);
action = ntohs(ah->type);
DBG_VVERB("action chk %d\n", action);
/* Currently supported: output port(s) action */
if ((action < 0) || (action > ACTION_LAST)) {
DBG_WARN("Unknown action id %d\n", action);
return false;
}
if (action_map[action] == ACTION_NOT_SUPPORTED) {
DBG_WARN("Action %d not supported\n", action);
return false;
}
/* Support front panel ports plus IN_PORT, ALL, FLOOD. */
if ((action == OFPAT_OUTPUT) || (action == OFPAT_ENQUEUE)) {
/* FIXME: For now, using the fact that enqueue and output
* actions are the same at the start of the structure
*/
dest_port = ntohs(oa->port);
if (dest_port == OFPP_TABLE) { /* Unsupported */
DBG_WARN("Warning: TABLE output action seen\n");
return false;
} else if (OF_PORT_IN_DP(hw_int, dest_port)) {
add_dport_to_extra(hw_int, dest_port, extra);
} else if ((dest_port == OFPP_FLOOD) || (dest_port == OFPP_ALL)) {
mcast_bitmaps_set(hw_int, extra, dest_port);
} else if (dest_port == OFPP_IN_PORT) {
if (src_is_wc) {
DBG_WARN("Warning: IN_PORT action on source wildcard\n");
return false;
}
add_dport_to_extra(hw_int, src_port, extra);
} else if (dest_port == OFPP_CONTROLLER) {
/* Controller/local are implemented with a "copy to CPU"
* action and a special reason code; Don't count as output port
*/
extra->local_reason |= CPU_REASON_TO_CONTROLLER;
} else if (dest_port == OFPP_LOCAL) {
extra->local_reason |= CPU_REASON_TO_LOCAL;
} else { /* NORMAL */
// DBG_WARN("Output action to port 0x%x not supported\n",
// dest_port);
// return false; /* FIXME: Ignore bad ports for now */
}
if (action == OFPAT_ENQUEUE) {
uint32 qid;
struct ofp_action_enqueue *ea;
ea = (struct ofp_action_enqueue *)p;
qid = ntohl(ea->queue_id);
if ((extra->cosq = of_hw_qid_find(dest_port, qid)) < 0) {
DBG_WARN("Warning: qid %d, port %d, map to cos failed\n",
qid, dest_port);
}
}
}
p += len;
actions_len -= len;
}
if (src_is_wc && (extra->dest_count > 1)) {
DBG_WARN("Warning: multi dest w/ src wildcard\n");
return false;
}
DBG_VERBOSE("Actions supported\n");
return true;
}
/****************************************************************
*
* FP Table management and manipulation routines
*
****************************************************************/
/*
* Functions related to hardware flow table manipulation and maintenance
*/
/* Set up field control structure; should be idempotent */
static void
field_control_init(void)
{
field_control_t *fc;
int idx;
fc = &field_control;
fc->entry_count = 0;
for (idx = 0; idx < FIELD_ENTRY_MAX; idx++) {
fc->entry_list[idx].in_use = 0;
fc->entry_list[idx].hw_flow = NULL;
fc->entry_list[idx].index = idx;
fc->entry_list[idx].last_counter = 0;
fc->entry_list[idx].counter_last_check = 0;
fc->entry_list[idx].total_counter = 0;
}
}
/* Alloc HW entry control structure, add qualification, actions and install */
static int
hw_entry_install(of_hw_driver_int_t *hw_int,
struct sw_flow *flow, hw_flow_t *hw_flow,
hw_flow_extra_t *extra)
{
/* Install HW entry */
FIXME;
}
/*
* hw_flow_install
*
* Install HW table entries corresponding to the given SW flow
*/
static int
hw_flow_install(of_hw_driver_int_t *hw_int, struct sw_flow *flow,
hw_flow_extra_t *extra)
{
hw_flow_t *hw_flow;
/* Allocate the HW flow object */
if ((hw_flow = hw_flow_create(flow)) == NULL) {
DBG_ERROR("failed to create hw flow struct\n");
return -1;
}
DBG_VERBOSE("hw flow install: sw %p. hw %p\n", flow, hw_flow);
TRY(hw_entry_install(hw_int, 0, flow, hw_flow, extra),
"local entry");
return 0;
}
/****************************************************************
*
* The driver APIs
*
****************************************************************/
/*
* Install a flow.
*
* First, check that the flow is supported; simultaneously, build
* up the "extra" information needed by the hardware for its installation.
* This includes bitmaps of the ports to which the packet will be
* forwarded (on local and remote devices if appropriate).
*
* Then look to see if the flow should overwrite an existing entry.
* If so, just remove that entry.
*
* Then call hw_flow_install which does all the actual HW changes
* based on the flow and on extra.
*/
/* NOTE: Returns 1 if flow installed, not normal error code */
int
of_hw_flow_install(struct sw_table *sw_tab, struct sw_flow *flow)
{
hw_flow_extra_t extra;
of_hw_driver_int_t *hw_int;
int hw_rc = 0;
int sw_insert_done = 0;
struct sw_flow *f;
hw_int = (of_hw_driver_int_t *)sw_tab;
DBG_VERBOSE("flow install %p\n", flow);
memset(&extra, 0, sizeof(extra));
extra.cosq = -1;
if (!actions_supported_check(hw_int, &flow->key, flow->sf_acts->actions,
flow->sf_acts->actions_len, &extra)) {
/* Unsupported actions */
DBG_VERBOSE("HW install failed: Unsupported actions\n");
return 0;
}
/* LOCK; */
/* Go through list looking for matching flows */
LIST_FOR_EACH (f, struct sw_flow, node, &hw_int->flows) {
if (f->priority == flow->priority
&& f->key.wildcards == flow->key.wildcards
&& flow_matches_2wild(&f->key, &flow->key)) {
/* Just remove the HW flow; install other below */
TRY_NR(hw_flow_remove(f), "hw_flow_remove for replace");
flow->serial = f->serial;
list_replace(&flow->node, &f->node);
list_replace(&flow->iter_node, &f->iter_node);
sw_insert_done = 1;
flow_free(f);
break;
}
if (f->priority < flow->priority) {
break;
}
}
/* ASSERT: sw_insert_done OR f points to insertion point for flow */
hw_rc = hw_flow_install(hw_int, flow, &extra);
if (hw_rc < 0) {
hw_int->insert_errors++;
if (sw_insert_done) { /* Remove from SW list */
list_remove(&flow->node);
list_remove(&flow->iter_node);
flow_free(flow);
hw_int->n_flows--;
}
} else {
hw_int->n_flows++;
hw_int->n_inserts++;
if (!sw_insert_done) {
flow->serial = hw_int->next_serial++;
list_insert(&f->node, &flow->node);
list_push_front(&hw_int->iter_flows, &flow->iter_node);
}
}
/* UNLOCK */
if (hw_rc < 0) {
DBG_WARN("Could not install flow in HW\n");
if (sw_insert_done) { /* Remove from SW list */
DBG_WARN("Removed matching flow but could not replace in HW\n");
}
return 0;
}
return 1;
}
/* FIXME: Change API to return list of deleted flows? */
/* Would make this easier */
/* Remove a flow or flows from the HW and SW tracking tables */
int
of_hw_flow_delete(struct datapath *dp, struct sw_table *sw_tab,
const struct sw_flow_key *key, uint16_t out_port,
uint16_t priority, int strict)
{
of_hw_driver_int_t *hw_int = (of_hw_driver_int_t *)sw_tab;
struct sw_flow *flow, *n;
int count = 0;
struct list deleted;
DBG_VERBOSE("delete: dp %p, idx %d, key %p, out 0x%x, prio %d, strict %d\n",
dp, hw_int->dp_idx, key, out_port, priority, strict);
list_init(&deleted);
/* LOCK; */
LIST_FOR_EACH_SAFE (flow, n, struct sw_flow, node, &hw_int->flows) {
if (flow_matches_desc(&flow->key, key, strict)
&& flow_has_out_port(flow, out_port)
&& (!strict || (flow->priority == priority))) {
TRY_NR(hw_flow_remove(flow), "hw flow remove");
list_remove(&flow->node);
list_remove(&flow->iter_node);
list_push_back(&deleted, &flow->node);
count++;
}
}
/* UNLOCK; */
/* Notify DP of deleted flows */
LIST_FOR_EACH_SAFE (flow, n, struct sw_flow, node, &deleted) {
dp_send_flow_end(dp, flow, flow->reason);
list_remove(&flow->node);
flow_free(flow);
}
return count;
}
/*
* Modify an existing flow
*
* We chicken out and just de-install/re-install in HW
*/
int
of_hw_flow_modify(struct sw_table *sw_tab, const struct sw_flow_key *key,
uint16_t priority, int strict,
const struct ofp_action_header *actions, size_t actions_len)
{
of_hw_driver_int_t *hw_int = (of_hw_driver_int_t *)sw_tab;
struct sw_flow *flow;
int count = 0;
hw_flow_extra_t extra;
memset(&extra, 0, sizeof(extra));
extra.cosq = -1;
#if defined(PLATFORM_HAS_REMOTES)
extra.new_vid = -1;
extra.new_pcp = -1;
#endif
if (!actions_supported_check(hw_int, &flow->key, actions,
actions_len, &extra)) {
/* Unsupported actions */
DBG_VERBOSE("Mod actions-supported failed: Unsupported actions\n");
return 0;
}
/* LOCK; */
LIST_FOR_EACH (flow, struct sw_flow, node, &hw_int->flows) {
if (flow_matches_desc(&flow->key, key, strict)
&& (!strict || (flow->priority == priority))) {
/* Change the flow, de-install from HW and re-install */
TRY_NR(hw_flow_remove(flow), "hw_flow_remove modify");
flow_replace_acts(flow, actions, actions_len);
TRY_NR(hw_flow_install(hw_int, flow, &extra),
"hw_flow_install modify");
/* FIXME: Clear stats on flow if updated? */
count++;
}
}
/* UNLOCK; */
return count;
}
/*
* Update stats
* Call sw tracking table timeout
* Iterate the deleted object list and call HW flow remove
*/
void
of_hw_flow_timeout(struct sw_table *sw_tab, struct list *deleted)
{
of_hw_driver_int_t *hw_int = (of_hw_driver_int_t *)sw_tab;
struct sw_flow *flow, *n;
int used;
uint64_t now = time_msec();
/* LOCK; */
/* FIXME */
LIST_FOR_EACH_SAFE (flow, n, struct sw_flow, node, &hw_int->flows) {
if (of_hw_sw_flow_stat_update(flow, &used, false) == 0) {
if (used) {
flow->used = now;
} else {
if (flow_timeout(flow)) {
DBG_VERBOSE("Flow %p expired\n", flow);
list_remove(&flow->node);
list_remove(&flow->iter_node);
list_push_back(deleted, &flow->node);
ASSERT(flow->private != NULL);
TRY_NR(hw_flow_remove(flow), "hw flow remove, timeout");
hw_int->n_flows--;
}
}
}
}
/* UNLOCK; */
}
struct sw_flow *
of_hw_flow_lookup(struct sw_table *sw_tab, const struct sw_flow_key *key)
{
of_hw_driver_int_t *hw_int = (of_hw_driver_int_t *)sw_tab;
struct sw_flow *flow;
LIST_FOR_EACH (flow, struct sw_flow, node, &hw_int->flows) {
if (flow_matches_1wild(key, &flow->key))
return flow;
}
return NULL;
}
int
of_hw_flow_stats_get(of_hw_driver_t *hw_drv, struct ofp_match match,
struct ofp_flow_stats **stats, int *count)
{
DBG_WARN("FIXME FLOW STATS GET\n");
return 0;
}
int
of_hw_aggregate_stats_get(struct ofp_match match,
struct ofp_aggregate_stats_reply *stats)
{
DBG_WARN("FIXME AGGREGATE STATS GET\n");
return 0;
}
void
of_hw_flow_remove_all(of_hw_driver_int_t *hw_int)
{
struct sw_flow *flow, *n;
/* FIXME: Other de-init? Keep count of DPs? */
LIST_FOR_EACH_SAFE (flow, n, struct sw_flow, node, &hw_int->flows) {
TRY_NR(hw_flow_remove(flow), "hw_flow_remove all");
list_remove(&flow->node);
list_remove(&flow->iter_node);
}
}
+111
View File
@@ -0,0 +1,111 @@
#ifndef SAMPLE_PLATFORM_HW_FLOW_H
#define SAMPLE_PLATFORM_HW_FLOW_H 1
/*
* Hardware Flow operations: The glue between software flows and
* actual hardware table entries.
*/
#include <openflow/openflow.h>
#include <of_hw_api.h>
#include "of_hw_platform.h"
/* DEBUG for HW flow lists */
#define HW_FLOW_MAGIC 0xba5eba11
#define HW_FLOW_MAKE_VALID(hf) (hf)->magic = HW_FLOW_MAGIC
#define HW_FLOW_IS_VALID(hf) \
(((hf) != NULL) && ((hf)->magic == HW_FLOW_MAGIC))
/* Some flows require multiple HW entries */
#define HW_FLOWS_PER_FLOW_MAX (2)
/* The glue between SW flows and HW entries */
typedef struct hw_flow {
struct sw_flow *flow; /* Corresponding SW flow */
of_hw_driver_t *hw_drv; /* DP for this flow; MAY BE NULL for internal */
int entry_count; /* SW flow may require multiple HW table entries */
entry_control_t *entry_list[HW_FLOWS_PER_FLOW_MAX];
uint32 hw_packet_count;
uint32 hw_byte_count;
uint32 magic; /* DEBUG */
} hw_flow_t;
/* Extra info needed by hardware about flow entry;
* determined during supported check; all in host order
*
* For smac, dmac, vid and priority, if these are changed at ingress
* and a remote rule is necessary, the remote rule must have the
* rewrite values to match. This is indicated if value is not -1.
*/
typedef struct hw_flow_extra_s {
int source_port; /* If single source port, what is it */
int dest_port; /* If one dest port, what is it */
/* If multi dest ports, one pbmp per device */
FIXME dports;
int dest_count; /* Drop (0), unicast (1), multicast (>1) */
uint32 local_reason; /* Why forwarded to CPU */
int cosq; /* For enqueue action */
} hw_flow_extra_t;
/* Driver functions */
extern int of_hw_flow_install(struct sw_table *flowtab, struct sw_flow *flow);
extern int of_hw_flow_modify(struct sw_table *flowtab,
const struct sw_flow_key *key, uint16_t priority, int strict,
const struct ofp_action_header *actions, size_t actions_len);
extern int of_hw_flow_delete(struct datapath *dp, struct sw_table *flowtab,
const struct sw_flow_key *key, uint16_t out_port,
uint16_t priority, int strict);
extern void of_hw_flow_timeout(struct sw_table *flowtab,
struct list *deleted);
extern struct sw_flow *of_hw_flow_lookup(struct sw_table *flowtab,
const struct sw_flow_key *key);
extern int of_hw_sw_flow_stat_update(struct sw_flow *flow, int *used,
int force_sync);
extern int of_hw_flow_stats_get(of_hw_driver_t *hw_drv, struct ofp_match,
struct ofp_flow_stats **stats, int *count);
extern int of_hw_aggregate_stats_get(struct ofp_match,
struct ofp_aggregate_stats_reply *stats);
/* Controls a single FP (HW table flow) entry */
struct entry_control_s {
/* These must be persistent; update fp_entry_remove if you change this */
int index; /* This entry-s index in field_control list */
int in_use;
hw_flow_t *hw_flow;
/*
* HW specific info
*/
FIXME;
unsigned long int used_check; /* Last counter; for checking if used */
unsigned long int last_counter; /* pkts or bytes, see stat_sel above */
unsigned long int counter_last_check; /* Track entry usage by cntr */
unsigned long int total_counter; /* FIXME: u64? */
};
/* Driver associated with a HW entry */
#define HW_ENTRY_DRIVER(ent) ((ent)->hw_flow->hw_drv)
/* FP control structure */
struct field_control_s {
/* HW Specific info */
FIXME;
int entry_count; /* How many entries in use */
entry_control_t entry_list[FIELD_ENTRY_MAX]; /* Instantiation of entries */
};
/* FIXME: Do we need reference counts on stat objects? */
extern int of_hw_fp_setup(void);
extern int of_hw_table_stats_update(of_hw_driver_int_t *hw_drv,
unsigned long *matched, unsigned long *missed);
extern void of_hw_flow_remove_all(of_hw_driver_int_t *hw_int);
#endif /* SAMPLE_PLATFORM_HW_FLOW_H */
+38
View File
@@ -0,0 +1,38 @@
#ifndef HW_LIB_SKELETON_PLATFORM_H
#define HW_LIB_SKELETON_PLATFORM_H 1
#if defined(OF_HW_DP_MAIN)
#define P printf
#else
#define P printf
#endif
/*
* General Hardware Switch platform defines
*
* Also includes platform specific definitions based on make defines
*/
#define OF_HW_MAX_PORT 64
/* Reasons that a packet is being forwarded to the controller; */
#define CPU_REASON_DEFAULT (1 << 0)
#define CPU_REASON_TO_CONTROLLER (1 << 1)
#define CPU_REASON_TO_LOCAL (1 << 2)
#define EXACT_MATCH_PRIORITY TBD
/****************************************************************
*
* Platform specific defines and linkage
*
* Implicitly we have a "driver" for the board which includes
* hardware specific information including HW to OF port mapping
*
****************************************************************/
#if defined(OF_SAMPLE_PLAT)
#include "sample_plat.h"
#endif
#endif /* HW_LIB_SKELETON_PLATFORM_H */
+29
View File
@@ -0,0 +1,29 @@
/*
* OS abstractions
*/
#ifndef OF_HW_OS_H
#define OF_HW_OS_H 1
#include <stdlib.h>
#define ALLOC(bytes) malloc(bytes)
#define FREE(ptr) free(ptr)
#define PKT_ALLOC(bytes) TBD
#define PKT_FREE(ptr) TBD
#define MUTEX_DECLARE(name) TBD
#define MUTEX_INIT(name) TBD
#define MUTEX_LOCK(name) TBD
#define MUTEX_UNLOCK(name) TBD
#define TIME_NOW(time) TBD
#define TIME_DIFF(diff, early, late) TBD
/* FIXME */
#include <ofpbuf.h>
#define os_pkt_free(pkt) ofpbuf_delete(pkt)
#endif /* OF_HW_OS_H */
+497
View File
@@ -0,0 +1,497 @@
/*
* Port related HW datapath functions
*/
#include <openflow/openflow.h>
#include <of_hw_api.h>
#include "xtoxll.h"
#include "hw_drv.h"
#include "of_hw_platform.h"
#include "port.h"
#include "debug.h"
/* Single set of HW port objects managed here; indexed by OF port num */
of_hw_port_t of_hw_ports[OF_HW_MAX_PORT];
/* Check if hw DP and port are valid and if port belongs to the DP */
#define CHECK_PORT(_drv, _p) do { \
if ((_drv) == NULL) return -1; \
if (((_p) < 1) || ((_p) > OF_HW_MAX_PORT)) { \
REPORT_ERROR("bad port number"); \
return -1; \
} \
if (!(OF_PORT_IN_DP(((of_hw_driver_int_t *)(_drv)), (_p)))) { \
REPORT_ERROR("DP does not own port"); \
return -1; \
} \
} while (0)
/*
* Queue configuration section
*
* Port queue: Per port structure for queue related information
* Queue map: Indicated queue is mapped and what OF qid its mapped to as
* well as queue properties (min-bw). Indexed by OF port number;
* per port instance of array
*
*/
typedef struct queue_map_s {
int in_use;
uint32_t qid; /* OF queue name */
int min_bw; /* OF value, tenths of a percent */
} queue_map_t;
typedef struct port_queue_s {
int speed; /* In Mbps */
queue_map_t queue_map[NUM_COS_QUEUES];
} port_queue_t;
static port_queue_t port_queue[OF_HW_MAX_PORT];
/* Return the cos for the queue matching qid if found; else -1.
* Assumes lock held
*/
static int
qid_find(int of_port, uint32_t qid)
{
struct queue_map_s *qm;
int cosq;
qm = port_queue[of_port].queue_map;
for (cosq = 0; cosq < NUM_COS_QUEUES; cosq++) {
if (qm[cosq].in_use && (qid == qm[cosq].qid)) {
return cosq;
}
}
return -1;
}
/* Return the cos for the queue matching qid if found; else -1.
* Assumes lock held
*/
int
of_hw_qid_find(int of_port, uint32_t qid)
{
int cosq;
/* LOCK */
cosq = qid_find(of_port, qid);
/* UNLOCK */
return cosq;
}
/*
* Map an OF qid to a COS queue index; if not present, add it if possible
* Assumes lock held
*/
static int
qid_find_add(int of_port, uint32_t qid)
{
struct queue_map_s *qm;
int cosq;
cosq = qid_find(of_port, qid);
if (cosq >= 0) {
return cosq;
}
/* Not found; add queue */
for (cosq = 0; cosq < NUM_COS_QUEUES; cosq++) {
qm = port_queue[of_port].queue_map;
if (!qm[cosq].in_use) {
qm[cosq].in_use = true;
qm[cosq].qid = qid;
return cosq;
}
}
return -1;
}
/*
* Port speed has changed; requires queue b/w to change
*/
static void
port_speed_change_reconfig(of_hw_driver_int_t *hw_int, int of_port)
{
int cosq;
struct queue_map_s *qm;
int rv;
/* LOCK */
for (cosq = 0; cosq < NUM_COS_QUEUES; cosq++) {
qm = port_queue[of_port].queue_map;
if (qm[cosq].in_use) {
/* FIXME: Calc and program values for HW for min-bw */
if (rv < 0) {
DBG_ERROR("ERROR: Set min BW for queue on port %d "
"link change\n", of_port);
}
}
}
/* UNLOCK */
}
/* Internal handler for port link status changes */
/* FIXME */
static void
of_hw_linkscan_handler(...)
{
int of_port;
of_hw_driver_int_t *hw_int;
/* Assumes speed, linkstatus from HW */
/* Map port to internal port object */
of_port = HW_MAP_TO_PORT();
if ((of_port < 0) || (of_port > OF_HW_MAX_PORT)) {
return;
}
/* LOCK */
hw_int = (of_hw_driver_int_t *)(of_hw_ports[of_port].owner);
if (hw_int != NULL) {
if (port_queue[of_port].speed != speed) {
port_queue[of_port].speed = speed;
port_speed_change_reconfig(hw_int, of_port);
}
}
/* Record in local struct */
of_hw_ports[of_port].link = linkstatus != 0;
if ((hw_int != NULL) && (hw_int->port_change != NULL)) {
hw_int->port_change(of_port, linkstatus != 0,
hw_int->port_change_cookie);
}
/* UNLOCK */
}
static int linkscan_registered;
/*
* of_hw_ports_init
* Set up hardware port map
*/
void
of_hw_ports_init(void)
{
int i;
for (i = 0; i < OF_HW_MAX_PORT; i++) {
of_hw_ports[i].owner = NULL;
of_hw_ports[i].port = MAP_OF_PORT_TO_HW_PORT(i);
}
/* FIXME: REGISTER FOR LINK CHANGE CALLBACK */
/* Always register linkscan handler */
linkscan_registered = 1;
/* Get current link status for each port; ignore errors for bad ports */
for (i = 0; i < OF_HW_MAX_PORT; i++) {
/* FIXME */
HW_LINK_STATUS_GET(of_hw_ports[i].port, &of_hw_ports[i].link);
}
}
#define HTON64 htonll
/* Get port stats into a standard openflow port stats structure */
int
of_hw_port_stats_get(of_hw_driver_t *hw_drv, int of_port,
struct ofp_port_stats *stats)
{
uint64 v1, v2;
CHECK_PORT(hw_drv, of_port);
#if 0 /* FIXME; Get stats from HW */
HW_STAT(of_port, snmpIfInNUcastPkts, &v1);
HW_STAT(of_port, snmpIfInUcastPkts, &v2);
v1 += v2;
stats->rx_packets = HTON64(v1);
HW_STAT(of_port, snmpIfOutNUcastPkts, &v1);
HW_STAT(of_port, snmpIfOutUcastPkts, &v2);
v1 += v2;
stats->tx_packets = HTON64(v1);
HW_STAT(of_port, snmpIfInOctets, &v1);
stats->rx_bytes = HTON64(v1);
HW_STAT(of_port, snmpIfOutOctets, &v1);
stats->tx_bytes = HTON64(v1);
HW_STAT(of_port, snmpIfInDiscards, &v1);
stats->rx_dropped = HTON64(v1);
HW_STAT(of_port, snmpIfOutDiscards, &v1);
stats->tx_dropped = HTON64(v1);
HW_STAT(of_port, snmpIfInErrors, &v1);
stats->rx_errors = HTON64(v1);
HW_STAT(of_port, snmpIfOutErrors, &v1);
stats->tx_errors = HTON64(v1);
#endif
v1 = UINT64_C(0xffffffffffffffff);
stats->rx_frame_err = HTON64(v1);
stats->rx_over_err = HTON64(v1);
stats->rx_crc_err = HTON64(v1);
stats->collisions = HTON64(v1);
return 0;
}
/*
* port_add/remove(table, port)
*
* The indicated port has been added to/removed from the datapath
* Add also maps the of_port number to the hw_port indicated
*
* SPEC CHANGE: If of_port passed is less than 0, use the passed
* port name to determine the port number and attach to that value;
*
* Returns the of_port number or -1 on error
*/
int
of_hw_port_add(of_hw_driver_t *hw_drv, int of_port, const char *hw_name)
{
int hw_idx;
of_hw_port_t *port_ctl;
of_hw_driver_int_t *hw_int;
if (hw_drv == NULL) {
return -1;
}
hw_idx = hw_port_name_to_index(hw_name);
if (of_port < 0) {
of_port = hw_idx + 1;
} else if (hw_idx + 1 != of_port) {
DBG_WARN("Add Port: OF port %d does not match name %s\n",
of_port, hw_name);
}
if (of_port >= OF_HW_MAX_PORT) {
DBG_ERROR("Add Port: Bad port number %d\n", of_port);
return -1;
}
hw_int = (of_hw_driver_int_t *)hw_drv;
port_ctl = &of_hw_ports[of_port];
HW_DRV_LOCK;
if (port_ctl->owner == NULL) {
port_ctl->owner = hw_drv;
/* FIXME: port bitmaps in HW structure */
} else if (port_ctl->owner != hw_drv) {
DBG_ERROR("Add Port: OF port %d owned by other DP\n", of_port);
} else {
DBG_WARN("Add Port: OF port %d already added\n", of_port);
}
HW_DRV_UNLOCK;
return of_port;
}
int
of_hw_port_remove(of_hw_driver_t *hw_drv, of_port_t of_port)
{
of_hw_port_t *port_ctl;
of_hw_driver_int_t *hw_int;
int rc = 0;
if (hw_drv == NULL) {
return -1;
}
if ((of_port < 1) || (of_port >= OF_HW_MAX_PORT)) {
DBG_ERROR("Remove Port: Bad port number %d\n", of_port);
return -1;
}
hw_int = (of_hw_driver_int_t *)hw_drv;
port_ctl = &of_hw_ports[of_port];
HW_DRV_LOCK;
if (port_ctl->owner == NULL) {
DBG_WARN("Remove Port: OF port %d already free\n", of_port);
} else if (port_ctl->owner != hw_drv) {
DBG_ERROR("Remove Port: OF port %d owned by other DP\n", of_port);
rc = -1;
} else {
port_ctl->owner = NULL;
}
/* FIXME UPDATE HW port bitmaps in hw_int */
HW_DRV_UNLOCK;
return rc;
}
/*
* port_link_get(table, port)
* port_enable_set(table, port, enable)
* port_enable_get(table, port)
*
* Get/set the indicated properties of a port. Only real ports
* set with port_add are supported.
*/
int
of_hw_port_link_get(of_hw_driver_t *hw_drv, int of_port)
{
int rc;
int link;
CHECK_PORT(hw_drv, of_port);
if (linkscan_registered) {
return of_hw_ports[of_port].link;
}
if ((rc = HW_LINK_GET(of_hw_ports[of_port].port, &link)) < 0) {
DBG_ERROR("link_get: error %d port %d\n", rc, of_port);
/* Return link down on error */
return 0;
}
return link ? 1 : 0;
}
int
of_hw_port_enable_set(of_hw_driver_t *hw_drv, int of_port, int enable)
{
int rc;
CHECK_PORT(hw_drv, of_port);
if ((rc = HW_PORT_ENABLE_SET(of_hw_ports[of_port].port, enable)) < 0) {
DBG_ERROR("of_hw_port_enable_set: error %d port %d\n",
rc, of_port);
return rc;
}
return 0;
}
int
of_hw_port_enable_get(of_hw_driver_t *hw_drv, int of_port)
{
int rc;
int enable;
CHECK_PORT(hw_drv, of_port);
if ((rc = HW_PORT_ENABLE_GET(of_hw_ports[of_port].port, &enable)) < 0) {
DBG_ERROR("of_hw_port_enable_get: error %d port %d\n",
rc, of_port);
return rc;
}
return enable ? 1 : 0;
}
/*
* port_change_register
*
* Register a callback function to receive port change notifications
* from ports in this datapath; only one callback per datapath is
* supported.
*/
int
of_hw_port_change_register(of_hw_driver_t *hw_drv, of_port_change_f callback,
void *cookie)
{
of_hw_driver_int_t *dp_int;
dp_int = (of_hw_driver_int_t *)hw_drv;
dp_int->port_change = callback;
dp_int->port_change_cookie = cookie;
return 0;
}
/*
* Init COS queue setup. The queues number is fixed at 8. Deficit
* round robin is the discipline, initially with all equal weights.
* As queues are configured with min bandwidth reservations, the
* weights are adjusted to ensure the requested targets.
*
* For now, these are device generic; may need to specialize in the
* future.
*/
int
of_hw_cos_setup(int num_cos)
{
/* FIXME: Set up COS queues */
return 0;
}
/*
* Add and/or configure an output queue on a port.
*
* If qid exists, update. If not, look for an unreferenced queue and
* set the qid to that value.
*
* Note that when a port state changes, may need to re-configure
* the bandwidth values for the port's queues.
*/
int
of_hw_port_queue_config(of_hw_driver_t *hw_drv, int of_port, uint32_t qid,
int min_bw) /* In tenths of a percent */
{
int min_kbps;
of_hw_driver_int_t *hw_int;
int cosq;
struct queue_map_s *qm;
int rv;
hw_int = (of_hw_driver_int_t *)hw_drv;
CHECK_PORT(hw_int, of_port);
/* LOCK */
/* Get the current bandwidth of the port (cached?) */
if ((cosq = qid_find_add(of_port, qid)) < 0) {
DBG_ERROR("Could not add queue: of port %d, qid %d\n", of_port, qid);
/* UNLOCK */
return -1;
}
qm = &port_queue[of_port].queue_map[qid];
qm->min_bw = min_bw;
HW_SET_MIN_BW(...);
/* UNLOCK */
return (rv != 0) ? -1 : 0;
}
/* Remove a queue from a port; this potentially affects the queue
* configuration otherwise we would not worry about it here.
* Return -1 if not found; 0 on success
*/
int
of_hw_port_queue_remove(of_hw_driver_t *hw_drv, int of_port, uint32_t qid)
{
int cosq;
(void)hw_drv;
/* LOCK */
if ((cosq = qid_find(of_port, qid)) == -1) {
/* UNLOCK */
return -1;
}
/* Do we need to update stats or DRR weights or anything? */
port_queue[of_port].queue_map[cosq].in_use = false;
/* UNLOCK */
return 0;
}
+45
View File
@@ -0,0 +1,45 @@
#ifndef OF_HW_PORT_H
#define OF_HW_PORT_H 1
#include <openflow/openflow.h>
#include <of_hw_api.h>
#include "of_hw_platform.h"
#define NUM_COS_QUEUES 8
/* Define port mapping object indexed by OF number */
typedef struct of_hw_port_s {
of_hw_driver_t *owner; /* Owner of this port */
int port; /* Physical port number on device */
int link; /* Link state */
/* TBD: Keep track of link state and don't transmit if down? */
/* Add other physical info here as needed */
} of_hw_port_t;
extern of_hw_port_t of_hw_ports[OF_HW_MAX_PORT];
#define FOREACH_DP_PORT(_idx, _drv) \
for (_idx = 0; _idx < OF_HW_MAX_PORT; _idx++) \
if (of_hw_ports[_idx].owner == (of_hw_driver_t *)(_drv))
extern int of_hw_cos_setup(int num_cos);
extern int of_hw_qid_find(int of_port, uint32_t qid);
extern void of_hw_ports_init(void);
extern int of_hw_port_stats_get(of_hw_driver_t *hw_drv, int of_port,
struct ofp_port_stats *stats);
extern int of_hw_port_add(of_hw_driver_t *hw_drv, int of_port,
const char *hw_name);
extern int of_hw_port_remove(of_hw_driver_t *hw_drv, of_port_t of_port);
extern int of_hw_port_link_get(of_hw_driver_t *hw_drv, int of_port);
extern int of_hw_port_enable_set(of_hw_driver_t *hw_drv, int of_port,
int enable);
extern int of_hw_port_enable_get(of_hw_driver_t *hw_drv, int of_port);
extern int of_hw_port_queue_config(of_hw_driver_t *hw_drv, int of_port,
uint32_t qid, int min_bw); /* In tenths of a percent */
extern int of_hw_port_queue_remove(of_hw_driver_t *hw_drv, int of_port,
uint32_t qid);
extern int of_hw_port_change_register(of_hw_driver_t *hw_drv,
of_port_change_f callback, void *cookie);
#endif /* OF_HW_PORT_H */
+82
View File
@@ -0,0 +1,82 @@
/*
* sample_plat.c
*
* FIXME: Standard confidential header
*
* $Id: $
*/
/*
* Sample hardware initialization functions
*/
#include "debug.h"
#include "of_hw_platform.h"
#if defined(SAMPLE_PLAT)
#include "port.h"
/* sample_plat_port_setup
*
* Set up linkscan and necessary spanning tree for ports
* Disable VLAN dropping
*/
static int
sample_plat_port_setup(...)
{
return 0;
}
#if defined(OF_HW_DP_MAIN) && defined(SAMPLE_HW_PLAT)
/* If defined, need to do init here */
/*
* sample_plat_pre_init
*
* Turn off everything that might cause problems during init
*/
static int
sample_plat_pre_init(void)
{
/* bring system down to known state */
return 0;
}
/*
*/
int
sample_plat_init(void)
{
/* Init system */
/* First, clear out everything */
sample_plat_pre_init();
/* ... */
sample_plat_port_setup(...);
return 0;
}
/* FIXME: Deal with initial FP setup */
#else /* Not stand alone init; Other code does init */
int
sample_plat_init(void)
{
DBG_WARN("sample_plat_init\n");
TRY(sample_plat_port_setup(...), "sample plat init port setup");
return 0;
}
#endif
#endif /* SAMPLE_PLAT */
+61
View File
@@ -0,0 +1,61 @@
#ifndef SAMPLE_PLAT_H
#define SAMPLE_PLAT_H 1
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <openflow/openflow.h>
#define OF_HW_MAX_PORTS 4
static inline int
of_port_to_hw_port(int of_port)
{
return of_port - 1;
}
/* Map port to OF port number */
static inline int
of_hw_port_to_of_port(int port)
{
if ((port < 0) || (port > 3)) {
return -1;
}
return port + 1;
}
#define _IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
/* Map name to hw port number: N => N-1
ge0 => 0 where "ge" can be any string and 0 can be any number
*/
static inline int
hw_port_name_to_index(const char *name)
{
if (_IS_DIGIT(name[0])) {
/* Treat as 1-based, OF number */
i = strtoul(&name[0], NULL, 10);
return i - 1;
}
len = strlen(name);
for (idx = 0; idx < len && !_IS_DIGIT(name[idx]); idx++) ;
if ((idx < len) && _IS_DIGIT(name[idx])) {
i = strtoul(&name[idx], NULL, 10);
return i;
}
return -1;
}
#undef _IS_DIGIT
#endif /* SAMPLE_PLAT_H */
+129
View File
@@ -0,0 +1,129 @@
/*
* Transmit and receive related functions for hardware platforms
*/
#include <of_hw_api.h>
#include "os.h"
#include "hw_drv.h"
#include "txrx.h"
#include "port.h"
#include "debug.h"
#include "of_hw_platform.h"
static int pkt_count, failures, prev_success, free_count, error_free;
static void
tx_pkt_callback(...)
{
of_packet_t *of_pkt = cookie;
/* os_pkt_free(of_pkt->os_pkt); */
FREE(of_pkt);
/* ... */
++free_count;
}
/*
* tx_packet_send(table, of_port, pkt, flags)
*
* Send packet to an openflow port.
*
* Proposed flags:
* APPLY_FLOW_TABLE: If set, and if the hardware supports
* it, send the packet through the flow table with the source
* port being the local CPU port. (Would be nice to have
* a flexible source port indicated; could hide in flags...)
*
* Assumes buffer in pkt struct can be used for sending data
*
*
*/
int
of_hw_packet_send(of_hw_driver_t *hw_drv, int of_port, of_packet_t *pkt,
uint32_t flags)
{
/* FIXME: Code to prepare and send pkt */
return 0;
}
/* Callback function registered with HW */
static int
of_hw_rx(...)
{
/* Sample RX handler */
int of_port;
of_hw_driver_int_t *hw_drv;
of_packet_t *of_pkt;
int pkt_len;
/* map receive port to of_port */
/* map received packet to of_pkt */
/* Call callback */
of_port = of_hw_port_to_of_port(receive_port);
if (of_port < 0) {
return hw_not_handled;
}
/* LOCK */
hw_drv = (of_hw_driver_int_t *)(of_hw_ports[of_port].owner);
if ((hw_drv == NULL) || (hw_drv->rx_handler == NULL)) {
/* UNLOCK */
return hw_not_handled;
}
of_pkt = ALLOC(sizeof(of_packet_t));
if (of_pkt == NULL) {
++hw_drv->rx_pkt_alloc_failures;
/* UNLOCK */
return hw_not_handled;
}
pkt_len = pkt->tot_len;
/* FIXME: FOR NOW, COPY DATA INTO NEW BUFFER; */
of_pkt->data = ALLOC(pkt_len);
if (of_pkt->data == NULL) {
FREE(of_pkt);
/* UNLOCK */
return hw_not_handled;
}
/* Handle VLAN tagging if needed */
/* Coy pkt data appropriately */
/* FIXME: Determine reason (dflt rule or directed) */
/* FIXME: Return code interpretation? */
/* OFPR_NO_MATCH, No matching flow. */
/* OFPR_ACTION Action explicitly output to controller. */
hw_drv->rx_handler(of_port, of_pkt, 0, hw_drv->rx_cookie);
/* UNLOCK */
return hw_handled;
}
static int rx_registered = 0;
/*
* packet_receive_register
*
* Register a callback function to receive packets from ports in
* this datapath
*/
int
of_hw_packet_receive_register(of_hw_driver_t *hw_drv,
of_packet_in_f callback, void *cookie)
{
of_hw_driver_int_t *dp_int;
int rv;
/* Register for link status changes */
if (!rx_registered) {
/* Set up low level pkt receive for callback */
rx_registered = 1;
}
dp_int = (of_hw_driver_int_t *)hw_drv;
dp_int->rx_handler = callback;
dp_int->rx_cookie = cookie;
return 0;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef OF_HW_TXRX_H
#define OF_HW_TXRX_H 1
#include <openflow/openflow.h>
#include <of_hw_api.h>
extern int of_hw_packet_send(of_hw_driver_t *hw_drv, int of_port,
of_packet_t *pkt, uint32_t flags);
extern int of_hw_packet_receive_register(of_hw_driver_t *hw_drv,
of_packet_in_f callback, void *cookie);
#endif /* OF_HW_TXRX_H */
+313
View File
@@ -0,0 +1,313 @@
/* Copyright (c) 2008, 2009, 2010 The Board of Trustees of The Leland Stanford
* Junior University
*
* We are making the OpenFlow specification and associated documentation
* (Software) available for public use and benefit with the expectation
* that others will use, modify and enhance the Software and contribute
* those enhancements back to the community. However, since we would
* like to make the Software available for broadest use, with as few
* restrictions as possible permission is hereby granted, free of
* charge, to any person obtaining a copy of this Software to deal in
* the Software under the copyrights without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* The name and trademarks of copyright holder(s) may NOT be used in
* advertising or publicity pertaining to the Software or any
* derivatives without specific, written prior permission.
*/
#if !defined(OF_HW_API_H)
#define OF_HW_API_H
/*
* OpenFlow hardware API definition
*
* This header file provides an abstraction of the flow table and
* port operations that can be used to build a driver for hardware
* that implements the OpenFlow protocol.
*
* Currently this driver depends (extends) the sw_table defined
* in the udatapath/table.h file. Hopefully that file will be
* moved up to library status to support kernel and userspace
* implementations.
*
*/
#include <openflow/openflow.h>
#include <udatapath/table.h> /* For sw_table */
/* REQUIRES:
* struct sw_table defined
* TBD: We could remove this restriction; it's mainly so that
* current chain.c operations can work. It also allows
* pointer coersion between the two types.
*
* Eventually, sw_table may be extended to include everything in
* this driver.
*
* pointer to struct datapath
*/
/**************** basic types ****************/
typedef uint32_t of_port_t;
typedef struct of_hw_driver of_hw_driver_t;
/**************** packet ****************/
/* The OpenFlow hardware packet abstraction */
typedef void *os_pkt_t; /* OS representation of packet */
/* Requires monolithic packet data */
typedef struct of_packet_s {
unsigned char *data; /* Pointer to packet data */
int length; /* Length in bytes */
os_pkt_t os_pkt; /* OS specific representation */
} of_packet_t;
/* Init an of_packet struct from an ofp_buffer struct */
#define OF_PKT_INIT(pkt, ofp_buf) do { \
(pkt)->data = (ofp_buf)->data; \
(pkt)->length = (ofp_buf)->size; \
(pkt)->os_pkt = (ofp_buf); \
} while (0)
/**************** callback protos ****************/
/* packet in callback function prototype */
typedef int (*of_packet_in_f)(of_port_t port,
of_packet_t *packet,
int reason,
void *cookie);
typedef void (*of_port_change_f)(of_port_t port,
int state,
void *cookie);
/****************************************************************
*
* Hardware Driver
*
****************************************************************/
/* Hardware capabilities structure */
typedef struct of_hw_driver_caps {
/* Proposed Flags:
* COUNT_PKTS_OR_BYTES Can count either pkts or bytes, not both
* INTERNAL_PRI Support internal priority mapping, and thus
* normal enqueuing action
* LOCAL_CPU_THRU_TABLE Can send packets from the CPU through
* the flow table
*/
uint32_t flags;
/* Number of fully qualified flows supported (approx) */
int max_flows;
uint32_t wc_supported; /* Bitmap of OFPFW_* supported wildcards */
uint32_t actions_supported; /* Bitmap of OFPAT_* supported actions */
uint32_t ofpc_flags; /* Bitmap of ofp_capabilities flags */
} of_hw_driver_caps_t;
enum of_hw_driver_flags {
OF_HW_DRV_COUNT_PKTS_OR_BYTES = 1 << 0,
OF_HW_DRV_INTERNAL_PRI = 1 << 1,
OF_HW_DRV_CPU_PKTS_THRU_TABLE = 1 << 2
};
/**************** Constructor/Destructor ****************/
extern of_hw_driver_t *new_of_hw_driver(struct datapath *dp);
extern void delete_of_hw_driver(of_hw_driver_t *hw_drv);
/* TBD: Add a HW/DP init function? */
/**************** HW DataPath Driver Structure ****************/
/* Extends sw_table */
struct of_hw_driver {
/*
* Notes on sw_table inheritance:
*
* See above as well
*
* n_lookup and n_matched are not dynamically updated, but the
* call to table_stats_update should set them
*/
struct sw_table sw_table;
/* HW datapath capabilities structure */
of_hw_driver_caps_t caps;
/* OPTIONAL
* init(table, flags)
*
* Initialize necessary hardware and software to run
* the switching table. Must be called prior to any other calls
* into the table (except maybe some ioctls?).
*
* Proposed flags include:
* BYTES/PACKETS: If COUNT_PKTS_OR_BYTES, which to count by default
* REATTACH: Inidicates HW was running, don't re-initialize HW
*
*/
int (*init)(of_hw_driver_t *hw_drv, uint32_t flags);
/*
* table_stats_get(table, stats)
* port_stats_get(port, stats)
* flow_stats_get(flow_desc, stats)
* aggregate_stats_get(flow_desc, stats)
*
* Fill out the stats object(s) for this table/port/flow(s)/set of flows
*
* Returns 0 on success.
*
* For all but flow_stats, the routine fills out a pre-allocated
* stats structure. For flow stats, an array of stats is allocated
* by the called routine with *count elements. It must be freed by
* the caller.
*
* (Optional? If count is NULL for flow_stats_get, find a single
* match with exactly the given ofp_match.)
*/
int (*table_stats_get)(of_hw_driver_t *hw_drv, struct
ofp_table_stats *stats);
int (*port_stats_get)(of_hw_driver_t *hw_drv, int of_port,
struct ofp_port_stats *stats);
int (*flow_stats_get)(of_hw_driver_t *hw_drv, struct ofp_match,
struct ofp_flow_stats **stats, int *count);
int (*aggregate_stats_get)(struct ofp_match,
struct ofp_aggregate_stats_reply *stats);
/*
* port_add/remove(table, port)
*
* The indicated port has been added to/removed from the datapath
* Add also maps the of_port number to the hw_port indicated
*/
int (*port_add)(of_hw_driver_t *hw_drv, int of_port, const char *hw_name);
int (*port_remove)(of_hw_driver_t *hw_drv, of_port_t port);
/*
* port_link_get(table, port)
* port_enable_set(table, port, enable)
* port_enable_get(table, port)
*
* Get/set the indicated properties of a port. Only real ports
* set with port_add are supported.
*/
int (*port_link_get)(of_hw_driver_t *hw_drv, int of_port);
int (*port_enable_set)(of_hw_driver_t *hw_drv, int of_port, int enable);
int (*port_enable_get)(of_hw_driver_t *hw_drv, int of_port);
/*
* port_queue_config(drv, port, qid, min-bw)
* port_queue_remove(drv, port, qid)
*
* Port queue control. Config will add the queue if not present
*/
int (*port_queue_config)(of_hw_driver_t *hw_drv, int of_port,
uint32_t qid, int min_bw);
int (*port_queue_remove)(of_hw_driver_t *hw_drv, int of_port,
uint32_t qid);
/*
* port_change_register
*
* Register a callback function to receive port change notifications
* from ports in this datapath
*/
int (*port_change_register)(of_hw_driver_t *hw_drv,
of_port_change_f callback, void *cookie);
/*
* packet_send(table, of_port, pkt, flags)
*
* Send packet to an openflow port.
*
* Proposed flags:
* APPLY_FLOW_TABLE: If set, and if the hardware supports
* it, send the packet through the flow table with the source
* port being the local CPU port. (Would be nice to have
* a flexible source port indicated; could hide in flags...)
*
* TBD: Owner of pkt and pkt->data after call; sync/async.
*/
int (*packet_send)(of_hw_driver_t *hw_drv, int of_port, of_packet_t *pkt,
uint32_t flags);
/*
* packet_receive_register
*
* Register a callback function to receive packets from ports in
* this datapath
*
* TBD: Semantics for owning packets and return codes so indicating.
*/
int (*packet_receive_register)(of_hw_driver_t *hw_drv,
of_packet_in_f callback, void *cookie);
/* OPTIONAL
* ioctl(table, request, io_param)
*
* Execute an ioctl on the table. A few ioctls are predefined,
* but most will be implementation specific.
* Returns 0 on success or an implementation specific other code.
*
* io_param is an input/output parameter whose value may be
* returned to the caller.
* io_len is the length of io_param in bytes.
* On input, the *io_param pointer may clobbered, so the caller must
* maintain it for deallocation if necessary.
* On output, when used -- which depends on the operation --
* the *io_param is a pointer to a buffer allocated by the ioctl
* routine, but owned by the calling routine.
*
* Question: Should a full I/O buffer be supported?
* ioctl(table, op, in_buf, in_len, out_buf, out_len); or
* ioctl(table, op, io_buf, io_len); where buf/len set on output.
*
* Proposed operations:
* Set debug level
* Clear port/flow/table stats
* Select packet or byte counter collection
*/
int (*ioctl)(of_hw_driver_t *hw_drv, uint32_t op, void **io_param,
int *io_len);
};
/**************** IOCTL values ****************/
enum of_hw_ioctl_e {
OF_HW_IOCTL_TABLE_DEBUG_SET = 1,
OF_HW_IOCTL_PORT_DEBUG_SET = 2,
OF_HW_IOCTL_BYTE_PKT_CNTR_SET = 3
};
/* Values for OF_HW_IOCTL_BYTE_PKT_CNTR_SET */
#define OF_HW_CNTR_PACKETS 0
#define OF_HW_CNTR_BYTES 1
enum of_hw_error_e {
OF_HW_OKAY = 0,
OF_HW_ERROR = -1,
OF_HW_PORT_DOWN = -2
};
#endif /* OF_HW_API_H */
+48
View File
@@ -1,3 +1,7 @@
#
# Build udatapath as binary
#
bin_PROGRAMS += udatapath/ofdatapath bin_PROGRAMS += udatapath/ofdatapath
man_MANS += udatapath/ofdatapath.8 man_MANS += udatapath/ofdatapath.8
@@ -22,6 +26,50 @@ udatapath_ofdatapath_SOURCES = \
udatapath/table-linear.c udatapath/table-linear.c
udatapath_ofdatapath_LDADD = lib/libopenflow.a $(SSL_LIBS) $(FAULT_LIBS) udatapath_ofdatapath_LDADD = lib/libopenflow.a $(SSL_LIBS) $(FAULT_LIBS)
udatapath_ofdatapath_CPPFLAGS = $(AM_CPPFLAGS)
EXTRA_DIST += udatapath/ofdatapath.8.in EXTRA_DIST += udatapath/ofdatapath.8.in
DISTCLEANFILES += udatapath/ofdatapath.8 DISTCLEANFILES += udatapath/ofdatapath.8
if BUILD_HW_LIBS
# Options for each platform
if NF2
udatapath_ofdatapath_LDADD += hw-lib/libnf2.a
udatapath_ofdatapath_CPPFLAGS += -DOF_HW_PLAT -DUSE_NETDEV -g
noinst_LIBRARIES += hw-lib/libnf2.a
endif
endif
if BUILD_HW_LIBS
#
# Build udatapath as a library
#
noinst_LIBRARIES += udatapath/libudatapath.a
udatapath_libudatapath_a_SOURCES = \
udatapath/chain.c \
udatapath/chain.h \
udatapath/crc32.c \
udatapath/crc32.h \
udatapath/datapath.c \
udatapath/datapath.h \
udatapath/dp_act.c \
udatapath/dp_act.h \
udatapath/of_ext_msg.c \
udatapath/of_ext_msg.h \
udatapath/udatapath.c \
udatapath/private-msg.c \
udatapath/private-msg.h \
udatapath/switch-flow.c \
udatapath/switch-flow.h \
udatapath/table.h \
udatapath/table-hash.c \
udatapath/table-linear.c
udatapath_libudatapath_a_CPPFLAGS = $(AM_CPPFLAGS)
udatapath_libudatapath_a_CPPFLAGS += -DOF_HW_PLAT -DUDATAPATH_AS_LIB -g
endif
+12
View File
@@ -38,6 +38,11 @@
#include <stdlib.h> #include <stdlib.h>
#include "switch-flow.h" #include "switch-flow.h"
#include "table.h" #include "table.h"
#include "datapath.h"
#if defined(OF_HW_PLAT)
#include <openflow/of_hw_api.h>
#endif
#define THIS_MODULE VLM_chain #define THIS_MODULE VLM_chain
#include "vlog.h" #include "vlog.h"
@@ -70,6 +75,13 @@ struct sw_chain *chain_create(struct datapath *dp)
return NULL; return NULL;
chain->dp = dp; chain->dp = dp;
#if defined(OF_HW_PLAT)
if (dp && dp->hw_drv) {
if (add_table(chain, (struct sw_table *)dp->hw_drv, 0) != 0) {
VLOG_ERR("Could not attach HW table to chain\n");
}
}
#endif
if (add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS, if (add_table(chain, table_hash2_create(0x1EDC6F41, TABLE_HASH_MAX_FLOWS,
0x741B8CD7, TABLE_HASH_MAX_FLOWS), 0x741B8CD7, TABLE_HASH_MAX_FLOWS),
0) 0)
+332 -32
View File
@@ -62,6 +62,73 @@
#define THIS_MODULE VLM_datapath #define THIS_MODULE VLM_datapath
#include "vlog.h" #include "vlog.h"
#if defined(OF_HW_PLAT)
#include <openflow/of_hw_api.h>
#include <pthread.h>
#endif
#if defined(OF_HW_PLAT) && !defined(USE_NETDEV)
/* Queue to decouple receive packet thread from rconn control thread */
/* Could make mutex per-DP */
static pthread_mutex_t pkt_q_mutex = PTHREAD_MUTEX_INITIALIZER;
#define PKT_Q_LOCK pthread_mutex_lock(&pkt_q_mutex)
#define PKT_Q_UNLOCK pthread_mutex_unlock(&pkt_q_mutex)
static void
enqueue_pkt(struct datapath *dp, struct ofpbuf *buffer, of_port_t port_no,
int reason)
{
struct hw_pkt_q_entry *q_entry;
if ((q_entry = xmalloc(sizeof(*q_entry))) == NULL) {
VLOG_WARN("Could not alloc q entry\n");
/* FIXME: Dealloc buffer */
return;
}
q_entry->buffer = buffer;
q_entry->next = NULL;
q_entry->port_no = port_no;
q_entry->reason = reason;
pthread_mutex_lock(&pkt_q_mutex);
if (dp->hw_pkt_list_head == NULL) {
dp->hw_pkt_list_head = q_entry;
} else {
dp->hw_pkt_list_tail->next = q_entry;
}
dp->hw_pkt_list_tail = q_entry;
pthread_mutex_unlock(&pkt_q_mutex);
}
/* If queue non-empty, fill out params and return 1; else return 0 */
static int
dequeue_pkt(struct datapath *dp, struct ofpbuf **buffer, of_port_t *port_no,
int *reason)
{
struct hw_pkt_q_entry *q_entry;
int rv = 0;
pthread_mutex_lock(&pkt_q_mutex);
q_entry = dp->hw_pkt_list_head;
if (dp->hw_pkt_list_head != NULL) {
dp->hw_pkt_list_head = dp->hw_pkt_list_head->next;
if (dp->hw_pkt_list_head == NULL) {
dp->hw_pkt_list_tail = NULL;
}
}
pthread_mutex_unlock(&pkt_q_mutex);
if (q_entry != NULL) {
rv = 1;
*buffer = q_entry->buffer;
*port_no = q_entry->port_no;
*reason = q_entry->reason;
free(q_entry);
}
return rv;
}
#endif
extern char mfr_desc; extern char mfr_desc;
extern char hw_desc; extern char hw_desc;
extern char sw_desc; extern char sw_desc;
@@ -156,7 +223,7 @@ dp_lookup_queue(struct sw_port *p, uint32_t queue_id)
struct sw_queue *q; struct sw_queue *q;
LIST_FOR_EACH(q, struct sw_queue, node, &p->queue_list) { LIST_FOR_EACH(q, struct sw_queue, node, &p->queue_list) {
if (q->queue_id == queue_id) { if ((q != NULL) && (q->queue_id == queue_id)) {
return q; return q;
} }
} }
@@ -175,6 +242,89 @@ gen_datapath_id(void)
return eth_addr_to_uint64(ea); return eth_addr_to_uint64(ea);
} }
/* FIXME: Should not depend on udatapath_as_lib */
#if defined(OF_HW_PLAT) && !defined(USE_NETDEV) && defined(UDATAPATH_AS_LIB)
/*
* Receive packet handling for hardware driver controlled ports
*
* FIXME: For now, call the pkt fwding directly; eventually may
* want to enqueue packets at this layer; at that point must
* make sure poll event is registered or timer kicked
*/
static int
hw_packet_in(of_port_t port_no, of_packet_t *packet, int reason,
void *cookie)
{
struct sw_port *port;
struct ofpbuf *buffer = NULL;
struct datapath *dp = (struct datapath *)cookie;
const int headroom = 128 + 2;
const int hard_header = VLAN_ETH_HEADER_LEN;
const int tail_room = sizeof(uint32_t); /* For crc if needed later */
VLOG_INFO("dp rcv packet on port %d, size %d\n",
port_no, packet->length);
if ((port_no < 1) || port_no > DP_MAX_PORTS) {
VLOG_ERR("Bad receive port %d\n", port_no);
/* TODO increment error counter */
return -1;
}
port = &dp->ports[port_no];
if (!PORT_IN_USE(port)) {
VLOG_WARN("Receive port not active: %d\n", port_no);
return -1;
}
if (!IS_HW_PORT(port)) {
VLOG_ERR("Receive port not controlled by HW: %d\n", port_no);
return -1;
}
/* Note: We're really not counting these for port stats as they
* should be gotten directly from the HW */
port->rx_packets++;
port->rx_bytes += packet->length;
/* For now, copy data into OFP buffer; eventually may steal packet
* from RX to avoid copy. As per dp_run, add headroom and offset bytes.
*/
buffer = ofpbuf_new(headroom + hard_header + packet->length + tail_room);
if (buffer == NULL) {
VLOG_WARN("Could not alloc ofpbuf on hw pkt in\n");
fprintf(stderr, "Could not alloc ofpbuf on hw pkt in\n");
} else {
buffer->data = (char*)buffer->data + headroom;
buffer->size = packet->length;
memcpy(buffer->data, packet->data, packet->length);
enqueue_pkt(dp, buffer, port_no, reason);
poll_immediate_wake();
}
return 0;
}
#endif
#if defined(OF_HW_PLAT)
static int
dp_hw_drv_init(struct datapath *dp)
{
dp->hw_pkt_list_head = NULL;
dp->hw_pkt_list_tail = NULL;
dp->hw_drv = new_of_hw_driver(dp);
if (dp->hw_drv == NULL) {
VLOG_ERR("Could not create HW driver");
return -1;
}
#if !defined(USE_NETDEV)
if (dp->hw_drv->packet_receive_register(dp->hw_drv,
hw_packet_in, dp) < 0) {
VLOG_ERR("Could not register with HW driver to receive pkts");
}
#endif
return 0;
}
#endif
int int
dp_new(struct datapath **dp_, uint64_t dpid) dp_new(struct datapath **dp_, uint64_t dpid)
{ {
@@ -190,6 +340,10 @@ dp_new(struct datapath **dp_, uint64_t dpid)
dp->listeners = NULL; dp->listeners = NULL;
dp->n_listeners = 0; dp->n_listeners = 0;
dp->id = dpid <= UINT64_C(0xffffffffffff) ? dpid : gen_datapath_id(); dp->id = dpid <= UINT64_C(0xffffffffffff) ? dpid : gen_datapath_id();
/* FIXME: Should not depend on udatapath_as_lib */
#if defined(OF_HW_PLAT) && (defined(UDATAPATH_AS_LIB) || defined(USE_NETDEV))
dp_hw_drv_init(dp);
#endif
dp->chain = chain_create(dp); dp->chain = chain_create(dp);
if (!dp->chain) { if (!dp->chain) {
VLOG_ERR("could not create chain"); VLOG_ERR("could not create chain");
@@ -272,6 +426,7 @@ new_port(struct datapath *dp, struct sw_port *port, uint16_t port_no,
list_init(&port->queue_list); list_init(&port->queue_list);
port->dp = dp; port->dp = dp;
port->flags |= SWP_USED;
port->netdev = netdev; port->netdev = netdev;
port->port_no = port_no; port->port_no = port_no;
port->num_queues = num_queues; port->num_queues = num_queues;
@@ -283,6 +438,48 @@ new_port(struct datapath *dp, struct sw_port *port, uint16_t port_no,
return 0; return 0;
} }
#if defined(OF_HW_PLAT) && !defined(USE_NETDEV)
int
dp_add_port(struct datapath *dp, const char *port_name, uint16_t num_queues)
{
int port_no;
int rc = 0;
struct sw_port *port;
fprintf(stderr, "Adding port %s. hw_drv is %p\n", port_name, dp->hw_drv);
if (dp->hw_drv && dp->hw_drv->port_add) {
port_no = dp->hw_drv->port_add(dp->hw_drv, -1, port_name);
if (port_no >= 0) {
port = &dp->ports[port_no];
if (port->flags & SWP_USED) {
VLOG_ERR("HW port %s (%d) already created\n",
port_name, port_no);
rc = -1;
} else {
fprintf(stderr, "Adding HW port %s as OF port number %d\n",
port_name, port_no);
/* FIXME: Determine and record HW addr, etc */
port->flags |= SWP_USED | SWP_HW_DRV_PORT;
port->dp = dp;
port->port_no = port_no;
list_init(&port->queue_list);
port->num_queues = num_queues;
strncpy(port->hw_name, port_name, sizeof(port->hw_name));
list_push_back(&dp->port_list, &port->node);
send_port_status(port, OFPPR_ADD);
}
} else {
VLOG_ERR("Port %s not recognized by hardware driver", port_name);
rc = -1;
}
} else {
VLOG_ERR("No hardware driver support; can't add ports");
rc = -1;
}
return rc;
}
#else /* Not HW platform support */
int int
dp_add_port(struct datapath *dp, const char *netdev, uint16_t num_queues) dp_add_port(struct datapath *dp, const char *netdev, uint16_t num_queues)
{ {
@@ -295,6 +492,7 @@ dp_add_port(struct datapath *dp, const char *netdev, uint16_t num_queues)
} }
return EXFULL; return EXFULL;
} }
#endif /* OF_HW_PLAT */
int int
dp_add_local_port(struct datapath *dp, const char *netdev, uint16_t num_queues) dp_add_local_port(struct datapath *dp, const char *netdev, uint16_t num_queues)
@@ -349,9 +547,27 @@ dp_run(struct datapath *dp)
} }
poll_timer_wait(1000); poll_timer_wait(1000);
#if defined(OF_HW_PLAT) && !defined(USE_NETDEV)
{ /* Process packets received from callback thread */
struct ofpbuf *buffer;
of_port_t port_no;
int reason;
struct sw_port *p;
while (dequeue_pkt(dp, &buffer, &port_no, &reason)) {
p = dp_lookup_port(dp, port_no);
/* FIXME: We're throwing away the reason that came from HW */
fwd_port_input(dp, buffer, p);
}
}
#endif
LIST_FOR_EACH_SAFE (p, pn, struct sw_port, node, &dp->port_list) { LIST_FOR_EACH_SAFE (p, pn, struct sw_port, node, &dp->port_list) {
int error; int error;
if (IS_HW_PORT(p)) {
continue;
}
if (!buffer) { if (!buffer) {
/* Allocate buffer with some headroom to add headers in forwarding /* Allocate buffer with some headroom to add headers in forwarding
* to the controller or adding a vlan tag, plus an extra 2 bytes to * to the controller or adding a vlan tag, plus an extra 2 bytes to
@@ -512,6 +728,9 @@ dp_wait(struct datapath *dp)
size_t i; size_t i;
LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) { LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
if (IS_HW_PORT(p)) {
continue;
}
netdev_recv_wait(p->netdev); netdev_recv_wait(p->netdev);
} }
LIST_FOR_EACH (r, struct remote, node, &dp->remotes) { LIST_FOR_EACH (r, struct remote, node, &dp->remotes) {
@@ -529,7 +748,7 @@ static int
output_all(struct datapath *dp, struct ofpbuf *buffer, int in_port, int flood) output_all(struct datapath *dp, struct ofpbuf *buffer, int in_port, int flood)
{ {
struct sw_port *p; struct sw_port *p;
int prev_port; int prev_port; /* Buffer is cloned for multiple transmits */
prev_port = -1; prev_port = -1;
LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) { LIST_FOR_EACH (p, struct sw_port, node, &dp->port_list) {
@@ -563,6 +782,32 @@ output_packet(struct datapath *dp, struct ofpbuf *buffer, uint16_t out_port,
q = NULL; q = NULL;
p = dp_lookup_port(dp, out_port); p = dp_lookup_port(dp, out_port);
/* FIXME: Needs update for queuing */
#if defined(OF_HW_PLAT) && !defined(USE_NETDEV)
if ((p != NULL) && IS_HW_PORT(p)) {
if (dp && dp->hw_drv) {
if (dp->hw_drv->port_link_get(dp->hw_drv, p->port_no)) {
of_packet_t *pkt;
int rv;
pkt = calloc(1, sizeof(*pkt));
OF_PKT_INIT(pkt, buffer);
rv = dp->hw_drv->packet_send(dp->hw_drv, out_port, pkt, 0);
if ((rv < 0) && (rv != OF_HW_PORT_DOWN)) {
VLOG_ERR("Error %d sending pkt on HW port %d\n",
rv, out_port);
ofpbuf_delete(buffer);
free(pkt);
}
}
}
return;
}
/* Fall through to software controlled ports if not HW port */
#endif
if (p && p->netdev != NULL) { if (p && p->netdev != NULL) {
if (!(p->config & OFPPC_PORT_DOWN)) { if (!(p->config & OFPPC_PORT_DOWN)) {
/* avoid the queue lookup for best-effort traffic */ /* avoid the queue lookup for best-effort traffic */
@@ -749,18 +994,41 @@ static void
fill_port_desc(struct sw_port *p, struct ofp_phy_port *desc) fill_port_desc(struct sw_port *p, struct ofp_phy_port *desc)
{ {
desc->port_no = htons(p->port_no); desc->port_no = htons(p->port_no);
strncpy((char *) desc->name, netdev_get_name(p->netdev), if (IS_HW_PORT(p)) {
sizeof desc->name); #if defined(OF_HW_PLAT) && !defined(USE_NETDEV)
desc->name[sizeof desc->name - 1] = '\0'; of_hw_driver_t *hw_drv;
memcpy(desc->hw_addr, netdev_get_etheraddr(p->netdev), ETH_ADDR_LEN);
hw_drv = p->dp->hw_drv;
strncpy((char *) desc->name, p->hw_name, sizeof desc->name);
desc->name[sizeof desc->name - 1] = '\0';
/* Update local port state */
if (hw_drv->port_link_get(hw_drv, p->port_no)) {
p->state &= ~OFPPS_LINK_DOWN;
} else {
p->state |= OFPPS_LINK_DOWN;
}
if (hw_drv->port_enable_get(hw_drv, p->port_no)) {
p->config &= ~OFPPC_PORT_DOWN;
} else {
p->config |= OFPPC_PORT_DOWN;
}
/* FIXME: Add current, supported and advertised features */
#endif
} else if (p->netdev) {
strncpy((char *) desc->name, netdev_get_name(p->netdev),
sizeof desc->name);
desc->name[sizeof desc->name - 1] = '\0';
memcpy(desc->hw_addr, netdev_get_etheraddr(p->netdev), ETH_ADDR_LEN);
desc->curr= htonl(netdev_get_features(p->netdev,
NETDEV_FEAT_CURRENT));
desc->supported = htonl(netdev_get_features(p->netdev,
NETDEV_FEAT_SUPPORTED));
desc->advertised = htonl(netdev_get_features(p->netdev,
NETDEV_FEAT_ADVERTISED));
desc->peer = htonl(netdev_get_features(p->netdev, NETDEV_FEAT_PEER));
}
desc->config = htonl(p->config); desc->config = htonl(p->config);
desc->state = htonl(p->state); desc->state = htonl(p->state);
desc->curr = htonl(netdev_get_features(p->netdev, NETDEV_FEAT_CURRENT));
desc->supported = htonl(netdev_get_features(p->netdev,
NETDEV_FEAT_SUPPORTED));
desc->advertised = htonl(netdev_get_features(p->netdev,
NETDEV_FEAT_ADVERTISED));
desc->peer = htonl(netdev_get_features(p->netdev, NETDEV_FEAT_PEER));
} }
static void static void
@@ -1453,10 +1721,10 @@ struct queue_stats_state {
}; };
static int static int
port_stats_init(const void *body, int body_len, void **state) port_stats_init(const void *body, int body_len UNUSED, void **state)
{ {
struct port_stats_state *s = xmalloc(sizeof *s); struct port_stats_state *s = xmalloc(sizeof *s);
struct ofp_port_stats_request *psr = body; const struct ofp_port_stats_request *psr = body;
s->start_port = 1; s->start_port = 1;
s->port_no = ntohs(psr->port_no); s->port_no = ntohs(psr->port_no);
@@ -1465,25 +1733,57 @@ port_stats_init(const void *body, int body_len, void **state)
} }
static void static void
dump_port_stats(struct sw_port *port, struct ofpbuf *buffer) dump_port_stats(struct datapath *dp, struct sw_port *port,
struct ofpbuf *buffer)
{ {
struct ofp_port_stats *ops = ofpbuf_put_uninit(buffer, sizeof *ops); struct ofp_port_stats *ops = ofpbuf_put_uninit(buffer, sizeof *ops);
ops->port_no = htons(port->port_no); ops->port_no = htons(port->port_no);
memset(ops->pad, 0, sizeof ops->pad); memset(ops->pad, 0, sizeof ops->pad);
ops->rx_packets = htonll(port->rx_packets); if (IS_HW_PORT(port)) {
ops->tx_packets = htonll(port->tx_packets); #if defined(OF_HW_PLAT) && !defined(USE_NETDEV)
ops->rx_bytes = htonll(port->rx_bytes); struct ofp_port_stats stats;
ops->tx_bytes = htonll(port->tx_bytes);
ops->rx_dropped = htonll(-1); memset(&stats, 0, sizeof(stats));
ops->tx_dropped = htonll(port->tx_dropped); if (dp->hw_drv->port_stats_get) {
ops->rx_errors = htonll(-1); if (dp->hw_drv->port_stats_get(dp->hw_drv, port->port_no,
ops->tx_errors = htonll(-1); &stats) < 0) {
ops->rx_frame_err = htonll(-1); VLOG_WARN("Error getting stats on port %d\n", port->port_no);
ops->rx_over_err = htonll(-1); return;
ops->rx_crc_err = htonll(-1); }
ops->collisions = htonll(-1); }
ops->rx_packets = htonll(stats.rx_packets);
ops->tx_packets = htonll(stats.tx_packets);
ops->rx_bytes = htonll(stats.rx_bytes);
ops->tx_bytes = htonll(stats.tx_bytes);
ops->rx_dropped = htonll(stats.rx_dropped);
ops->tx_dropped = htonll(stats.tx_dropped);
ops->rx_errors = htonll(stats.rx_errors);
ops->tx_errors = htonll(stats.tx_errors);
ops->rx_frame_err = htonll(stats.rx_frame_err);
ops->rx_over_err = htonll(stats.rx_over_err);
ops->rx_crc_err = htonll(stats.rx_crc_err);
ops->collisions = htonll(stats.collisions);
#endif
} else {
ops->rx_packets = htonll(port->rx_packets);
ops->tx_packets = htonll(port->tx_packets);
ops->rx_bytes = htonll(port->rx_bytes);
ops->tx_bytes = htonll(port->tx_bytes);
ops->rx_dropped = htonll(-1);
ops->tx_dropped = htonll(port->tx_dropped);
ops->rx_errors = htonll(-1);
ops->tx_errors = htonll(-1);
ops->rx_frame_err = htonll(-1);
ops->rx_over_err = htonll(-1);
ops->rx_crc_err = htonll(-1);
ops->collisions = htonll(-1);
}
} }
/* Although this makes some of the motions of being called
* multiple times preserving state, it doesn't actually support
* that process; the for loop can never break early.
*/
static int port_stats_dump(struct datapath *dp, void *state, static int port_stats_dump(struct datapath *dp, void *state,
struct ofpbuf *buffer) struct ofpbuf *buffer)
{ {
@@ -1495,18 +1795,18 @@ static int port_stats_dump(struct datapath *dp, void *state,
/* Dump statistics for all ports */ /* Dump statistics for all ports */
for (i = s->start_port; i < DP_MAX_PORTS; i++) { for (i = s->start_port; i < DP_MAX_PORTS; i++) {
p = dp_lookup_port(dp, i); p = dp_lookup_port(dp, i);
if (p && p->netdev) { if (p && PORT_IN_USE(p)) {
dump_port_stats(p, buffer); dump_port_stats(dp, p, buffer);
} }
} }
if (dp->local_port) { if (dp->local_port) {
dump_port_stats(dp->local_port, buffer); dump_port_stats(dp, dp->local_port, buffer);
} }
} else { } else {
/* Dump statistics for a single port */ /* Dump statistics for a single port */
p = dp_lookup_port(dp, s->port_no); p = dp_lookup_port(dp, s->port_no);
if (p && p->netdev) { if (p && PORT_IN_USE(p)) {
dump_port_stats(p, buffer); dump_port_stats(dp, p, buffer);
} }
} }
+40
View File
@@ -44,6 +44,11 @@
#include "list.h" #include "list.h"
#include "netdev.h" #include "netdev.h"
/* FIXME: Can declare struct of_hw_driver instead */
#if defined(OF_HW_PLAT)
#include <openflow/of_hw_api.h>
#endif
struct rconn; struct rconn;
struct pvconn; struct pvconn;
struct sw_flow; struct sw_flow;
@@ -62,11 +67,26 @@ struct sw_queue {
uint16_t min_rate; uint16_t min_rate;
}; };
#define MAX_HW_NAME_LEN 32
enum sw_port_flags {
SWP_USED = 1 << 0, /* Is port being used */
SWP_HW_DRV_PORT = 1 << 1, /* Port controlled by HW driver */
};
#if defined(OF_HW_PLAT) && !defined(USE_NETDEV)
#define IS_HW_PORT(p) ((p)->flags & SWP_HW_DRV_PORT)
#else
#define IS_HW_PORT(p) 0
#endif
#define PORT_IN_USE(p) (((p) != NULL) && (p)->flags & SWP_USED)
struct sw_port { struct sw_port {
uint32_t config; /* Some subset of OFPPC_* flags. */ uint32_t config; /* Some subset of OFPPC_* flags. */
uint32_t state; /* Some subset of OFPPS_* flags. */ uint32_t state; /* Some subset of OFPPS_* flags. */
uint32_t flags; /* SWP_* flags above */
struct datapath *dp; struct datapath *dp;
struct netdev *netdev; struct netdev *netdev;
char hw_name[OFP_MAX_PORT_NAME_LEN];
struct list node; /* Element in datapath.ports. */ struct list node; /* Element in datapath.ports. */
unsigned long long int rx_packets, tx_packets; unsigned long long int rx_packets, tx_packets;
unsigned long long int rx_bytes, tx_bytes; unsigned long long int rx_bytes, tx_bytes;
@@ -78,6 +98,15 @@ struct sw_port {
struct list queue_list; /* list of all queues for this port */ struct list queue_list; /* list of all queues for this port */
}; };
#if defined(OF_HW_PLAT)
struct hw_pkt_q_entry {
struct ofpbuf *buffer;
struct hw_pkt_q_entry *next;
of_port_t port_no;
int reason;
};
#endif
#define DP_MAX_PORTS 255 #define DP_MAX_PORTS 255
BUILD_ASSERT_DECL(DP_MAX_PORTS <= OFPP_MAX); BUILD_ASSERT_DECL(DP_MAX_PORTS <= OFPP_MAX);
@@ -105,6 +134,15 @@ struct datapath {
struct sw_port ports[DP_MAX_PORTS]; struct sw_port ports[DP_MAX_PORTS];
struct sw_port *local_port; /* OFPP_LOCAL port, if any. */ struct sw_port *local_port; /* OFPP_LOCAL port, if any. */
struct list port_list; /* All ports, including local_port. */ struct list port_list; /* All ports, including local_port. */
#if defined(OF_HW_PLAT)
/* Although the chain maintains the pointer to the HW driver
* for flow operations, the datapath needs the port functions
* in the driver structure
*/
of_hw_driver_t *hw_drv;
struct hw_pkt_q_entry *hw_pkt_list_head, *hw_pkt_list_tail;
#endif
}; };
int dp_new(struct datapath **, uint64_t dpid); int dp_new(struct datapath **, uint64_t dpid);
@@ -124,4 +162,6 @@ void dp_output_control(struct datapath *, struct ofpbuf *, int in_port,
struct sw_port * dp_lookup_port(struct datapath *, uint16_t); struct sw_port * dp_lookup_port(struct datapath *, uint16_t);
struct sw_queue * dp_lookup_queue(struct sw_port *, uint32_t); struct sw_queue * dp_lookup_queue(struct sw_port *, uint32_t);
int udatapath_cmd(int argc, char *argv[]);
#endif /* datapath.h */ #endif /* datapath.h */
+14 -2
View File
@@ -142,7 +142,7 @@ recv_of_exp_queue_modify(struct datapath *dp,
struct ofp_packet_queue *opq; struct ofp_packet_queue *opq;
struct ofp_queue_prop_min_rate *mr; struct ofp_queue_prop_min_rate *mr;
int error; int error = 0;
uint16_t port_no; uint16_t port_no;
uint32_t queue_id; uint32_t queue_id;
@@ -167,7 +167,7 @@ recv_of_exp_queue_modify(struct datapath *dp,
queue_id = ntohl(opq->queue_id); queue_id = ntohl(opq->queue_id);
p = dp_lookup_port(dp, port_no); p = dp_lookup_port(dp, port_no);
if (p->netdev){ if (PORT_IN_USE(p)) {
q = dp_lookup_queue(p, queue_id); q = dp_lookup_queue(p, queue_id);
if (q) { if (q) {
/* queue exists - modify it */ /* queue exists - modify it */
@@ -208,6 +208,18 @@ recv_of_exp_queue_modify(struct datapath *dp,
VLOG_ERR("Failed to create/modify queue - port %d doesn't exist", VLOG_ERR("Failed to create/modify queue - port %d doesn't exist",
port_no); port_no);
} }
if (!error) {
if (IS_HW_PORT(p)) {
#if defined(OF_HW_PLAT) && !defined(USE_NETDEV)
error = dp->hw_drv->port_queue_config(dp->hw_drv, port_no,
queue_id, ntohs(mr->rate));
if (error < 0) {
VLOG_ERR("Failed to update HW port %d queue %d",
port_no, queue_id);
}
#endif
}
}
} }
/** /**
* Parses a set dp_desc message and uses it to set * Parses a set dp_desc message and uses it to set
+2
View File
@@ -75,6 +75,8 @@ struct sw_flow {
struct list node; struct list node;
struct list iter_node; struct list iter_node;
unsigned long int serial; unsigned long int serial;
void *private; /* Cookie for tables */
}; };
int flow_matches_1wild(const struct sw_flow_key *, const struct sw_flow_key *); int flow_matches_1wild(const struct sw_flow_key *, const struct sw_flow_key *);
+1 -1
View File
@@ -39,8 +39,8 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include "datapath.h"
struct datapath; /* Forward declaration for delete operation */
struct sw_flow; struct sw_flow;
struct sw_flow_key; struct sw_flow_key;
struct ofp_action_header; struct ofp_action_header;
+26 -4
View File
@@ -55,6 +55,10 @@
#include "vconn-ssl.h" #include "vconn-ssl.h"
#include "vlog-socket.h" #include "vlog-socket.h"
#if defined(OF_HW_PLAT)
#include <openflow/of_hw_api.h>
#endif
#define THIS_MODULE VLM_udatapath #define THIS_MODULE VLM_udatapath
#include "vlog.h" #include "vlog.h"
@@ -77,8 +81,26 @@ static uint16_t num_queues = NETDEV_MAX_QUEUES;
static void add_ports(struct datapath *dp, char *port_list); static void add_ports(struct datapath *dp, char *port_list);
/* Need to treat this more generically */
#if defined(UDATAPATH_AS_LIB)
#define OFP_FATAL(_er, _str, args...) do { \
fprintf(stderr, _str, ## args); \
return -1; \
} while (0)
#else
#define OFP_FATAL(_er, _str, args...) ofp_fatal(_er, _str, ## args)
#endif
#if !defined(UDATAPATH_AS_LIB)
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{
return udatapath_cmd(argc, argv);
}
#endif
int
udatapath_cmd(int argc, char *argv[])
{ {
int n_listeners; int n_listeners;
int error; int error;
@@ -92,7 +114,7 @@ main(int argc, char *argv[])
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
if (argc - optind < 1) { if (argc - optind < 1) {
ofp_fatal(0, "at least one listener argument is required; " OFP_FATAL(0, "at least one listener argument is required; "
"use --help for usage"); "use --help for usage");
} }
@@ -113,7 +135,7 @@ main(int argc, char *argv[])
} }
} }
if (!n_listeners) { if (!n_listeners) {
ofp_fatal(0, "could not listen for any connections"); OFP_FATAL(0, "could not listen for any connections");
} }
if (port_list) { if (port_list) {
@@ -122,13 +144,13 @@ main(int argc, char *argv[])
if (local_port) { if (local_port) {
error = dp_add_local_port(dp, local_port, 0); error = dp_add_local_port(dp, local_port, 0);
if (error) { if (error) {
ofp_fatal(error, "failed to add local port %s", local_port); OFP_FATAL(error, "failed to add local port %s", local_port);
} }
} }
error = vlog_server_listen(NULL, NULL); error = vlog_server_listen(NULL, NULL);
if (error) { if (error) {
ofp_fatal(error, "could not listen for vlog connections"); OFP_FATAL(error, "could not listen for vlog connections");
} }
die_if_already_running(); die_if_already_running();