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:
1 parent
b70d37144f
commit
6778bd8cdf
26 files changed
+3133
-40
No files matched your search
+2
-1
@@ -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
|
||||||
@@ -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],
|
||||||
|
|||||||
@@ -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])
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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 */
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 */
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 */
|
||||||
@@ -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 */
|
||||||
@@ -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 */
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 */
|
||||||
@@ -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 */
|
||||||
@@ -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 */
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 */
|
||||||
@@ -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 */
|
||||||
@@ -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
|
||||||
@@ -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
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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();
|
||||||
|
|||||||
Reference in new issue
Block a user