8000 Add virtual network switch component by alexandermbrown · Pull Request #177 · au-ts/sddf · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add virtual network switch component #177

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions include/sddf/network/mac802.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2024, UNSW (ABN 57 195 873 179)
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <stdint.h>
#include <sddf/util/printf.h>

#define PR_MAC802_ADDR "%x:%x:%x:%x:%x:%x"

/* Expects a *pointer* to a struct ether_addr */
#define PR_MAC802_ADDR_ARGS(a, dir) (a)->ether_##dir##_addr_octet[0], \
(a)->ether_##dir##_addr_octet[1], \
(a)->ether_##dir##_addr_octet[2], \
(a)->ether_##dir##_addr_octet[3], \
(a)->ether_##dir##_addr_octet[4], \
(a)->ether_##dir##_addr_octet[5]

#define PR_MAC802_DEST_ADDR_ARGS(a) PR_MAC802_ADDR_ARGS(a, dest)
#define PR_MAC802_SRC_ADDR_ARGS(a) PR_MAC802_ADDR_ARGS(a, src)

#define MAC802_BYTES 6

/* This is a name for the 96 bit ethernet addresses available on many
systems. */
struct ether_addr {
uint8_t ether_dest_addr_octet[MAC802_BYTES];
uint8_t ether_src_addr_octet[MAC802_BYTES];
uint8_t etype[2]; // Ethertype
uint8_t payload[46];
uint8_t crc[4];
} __attribute__ ((__packed__));


static inline bool mac802_addr_eq_num(const uint8_t *addr0, const uint8_t *addr1, unsigned int num)
{
for (int i = 0; i < num; i++) {
if (addr0[i] != addr1[i]) {
return false;
}
}
return true;
}

static inline bool mac802_addr_eq(const uint8_t *addr0, const uint8_t *addr1)
{
return mac802_addr_eq_num(addr0, addr1, MAC802_BYTES);
}

static inline bool mac802_addr_is_bcast(const uint8_t *addr)
{
const uint8_t bcast_macaddr[MAC802_BYTES] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
return mac802_addr_eq(addr, bcast_macaddr);
}
28 changes: 28 additions & 0 deletions include/sddf/network/vswitch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once
#include <microkit.h>
#include <sddf/network/queue.h>

#define VSWITCH_MAX_PORT_COUNT 64

typedef uint64_t vswitch_port_bitmap_t;

typedef struct vswitch_channel {
net_queue_handle_t q;
microkit_channel ch;
char *data_region;
} vswitch_channel_t;

/* These ports are analogous to ports on a physical switch.
* They represent a medium you can send to and receive from. */
typedef struct vswitch_port {
/* For clients, incoming is TX, outgoing is RX.
* For virtualisers, incoming is RX, outgoing is TX. */
vswitch_channel_t incoming;
vswitch_channel_t outgoing;
vswitch_port_bitmap_t allow_list;
} vswitch_port_t;

static inline bool vswitch_can_send_to(vswitch_port_t *src, int dst)
{
return src->allow_list & (1 << (uint64_t)dst);
}
1 change: 1 addition & 0 deletions network/components/copy.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <sddf/util/string.h>
#include <sddf/util/util.h>
#include <sddf/util/printf.h>
#include <sddf/util/string.h>
#include <ethernet_config.h>

#define VIRT_RX_CH 0
Expand Down
6 changes: 5 additions & 1 deletion network/components/virt_rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,11 @@ void rx_return(void)
//
// [1]: https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Instructions/DC-IVAC--Data-or-unified-Cache-line-Invalidate-by-VA-to-PoC
cache_clean_and_invalidate(buffer_vaddr, buffer_vaddr + buffer.len);
int client = get_mac_addr_match((struct ethernet_header *) buffer_vaddr);
// int client = get_mac_addr_match((struct ethernet_header *) buffer_vaddr);

// Layer 2 forwarding now happens in the vswitch
int client = 0;

if (client == BROADCAST_ID) {
int ref_index = buffer.io_or_offset / NET_BUFFER_SIZE;
assert(buffer_refs[ref_index] == 0);
Expand Down
2 changes: 1 addition & 1 deletion network/components/virt_tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ void init(void)

/* CDTODO: Can we make this system agnostic? */
state.buffer_region_paddrs[0] = buffer_data_region_cli0_paddr;
state.buffer_region_paddrs[1] = buffer_data_region_cli1_paddr;
// state.buffer_region_paddrs[1] = buffer_data_region_cli1_paddr;

tx_provide();
}
169 changes: 169 additions & 0 deletions network/components/vswitch/vswitch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Copyright 2024, UNSW
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <stdbool.h>
#include <stdint.h>
#include <microkit.h>
#include <sddf/network/vswitch.h>
#include <sddf/network/queue.h>
#include <sddf/network/mac802.h>
#include <sddf/network/util.h>
#include <sddf/util/fence.h>
#include <sddf/util/util.h>
#include <sddf/util/printf.h>
#define VSWITCH_IMPL
#include <vswitch_config.h>

_Static_assert(VSWITCH_PORT_COUNT <= VSWITCH_MAX_PORT_COUNT, "Too many ports");
#define UNKNOWN_PORT -1

typedef struct mac_addr {
uint8_t addr[6];
} mac_addr_t;

typedef struct channel_map {
// Store macs contiguously for better cache performance during lookup.
mac_addr_t macs[VSWITCH_LOOKUP_SIZE];
uint8_t port[VSWITCH_LOOKUP_SIZE];
uint32_t len;
} channel_map_t;

static struct {
vswitch_port_t ports[VSWITCH_PORT_COUNT];
channel_map_t map;
} vswitch;


static int channel_map_find(channel_map_t *map, const uint8_t *dest_macaddr)
{
for (int i = 0; i < map->len; i++) {
if (mac802_addr_eq(map->macs[i].addr, dest_macaddr)) {
return map->port[i];
}
}
return UNKNOWN_PORT;
}

static bool channel_map_add(channel_map_t *map, const uint8_t *macaddr, int port)
{
if (map->len >= VSWITCH_LOOKUP_SIZE) {
sddf_dprintf("VSWITCH: Channel map is full\n");
return false;
}
if (channel_map_find(map, macaddr) != UNKNOWN_PORT) {
return false;
}
if (mac802_addr_is_bcast(macaddr)) {
return false;
}

sddf_memcpy(map->macs[map->len].addr, macaddr, 6);
map->port[map->len] = port;
map->len++;
return true;
}

static void forward_frame(vswitch_channel_t *src, vswitch_channel_t *dest, net_buff_desc_t *src_buf)
{
const char *src_data = src->data_region + src_buf->io_or_offset;

net_buff_desc_t d_buffer;
if (net_dequeue_free(&dest->q, &d_buffer) != 0) {
return;
}
// TODO: make fast memcpy
1E80 sddf_memcpy(dest->data_region + d_buffer.io_or_offset, src_data, src_buf->len);

d_buffer.len = src_buf->len;
net_enqueue_active(&dest->q, d_buffer);

if (net_require_signal_active(&dest->q)) {
net_cancel_signal_active(&dest->q);
microkit_notify(dest->ch);
}
}

static void try_broadcast(int src_port, vswitch_port_t *src, net_buff_desc_t *buffer)
{
for (int i = 0; i < VSWITCH_PORT_COUNT; i++) {
if (i != src_port && vswitch_can_send_to(src, i)) {
vswitch_channel_t *outgoing = &vswitch.ports[i].outgoing;
forward_frame(&src->incoming, outgoing, buffer);
}
}
}

static void try_send(vswitch_port_t *src, const uint8_t *dest_macaddr, net_buff_desc_t *buffer)
{
int dest_port = channel_map_find(&vswitch.map, dest_macaddr);

if (dest_port == UNKNOWN_PORT || !vswitch_can_send_to(src, dest_port)) {
return;
}

vswitch_channel_t *outgoing = &vswitch.ports[dest_port].outgoing;
forward_frame(&src->incoming, outgoing, buffer);
}

static void notified_by_port(int port)
{
net_buff_desc_t sddf_buffer;

vswitch_port_t *src = &vswitch.ports[port];
net_queue_handle_t *src_queue = &src->incoming.q;

bool reprocess = true;
while (reprocess) {
while (net_dequeue_active(src_queue, &sddf_buffer) != -1) {
const char *frame_data = src->incoming.data_region + sddf_buffer.io_or_offset;
const struct ether_addr *macaddr = (void *)frame_data;

// Add forwarding table entry from source MAC to port
channel_map_add(&vswitch.map, macaddr->ether_src_addr_octet, port);

// Forward frame
if (mac802_addr_is_bcast(macaddr->ether_dest_addr_octet)) {
try_broadcast(port, src, &sddf_buffer);
} else {
try_send(src, macaddr->ether_dest_addr_octet, &sddf_buffer);
}

sddf_buffer.len = 0;
net_enqueue_free(src_queue, sddf_buffer);
}

net_request_signal_active(src_queue);
reprocess = false;

if (!net_queue_empty_active(src_queue)) {
net_cancel_signal_active(src_queue);
reprocess = true;
}
}
}

void notified(microkit_channel ch)
{
// This could be optimised but port count is usually low.
for (int port = 0; port < VSWITCH_PORT_COUNT; port++) {
if (ch == vswitch.ports[port].incoming.ch) {
notified_by_port(port);
return;
}
if (ch == vswitch.ports[port].outgoing.ch) {
// TODO: check if anyone is waiting for room in this port?
// Might be better to drop packets in this case.
return;
}
}
}

void init(void)
{
for (int i = 0; i < VSWITCH_PORT_COUNT; i++) {
net_vswitch_init_port(i, &vswitch.ports[i]);
net_buffers_init(&vswitch.ports[i].outgoing.q, 0);
}
}
49 changes: 49 additions & 0 deletions network/components/vswitch/vswitch.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#
# Copyright 2024, UNSW
#
# SPDX-License-Identifier: BSD-2-Clause
#
# This Makefile snippet builds the network components
# (for example, simple RX and TX virtualisers)
# it should be included into your project Makefile
#
# NOTES:
# Generates vswitch.elf
# Requires ${SDDF}/util/util.mk to build the utility library for debug output


VSWITCH_COMPONENTS_DIR := $(abspath $(dir $(lastword ${MAKEFILE_LIST})))
VSWITCH_IMAGES := vswitch.elf
network/components/vswitch/%.o: ${SDDF}/network/components/%.c
${CC} ${CFLAGS} -c -o $@ $<

VSWITCH_COMPONENT_OBJ := network/components/vswitch/vswitch.o

CFLAGS_vswitch += ${NUM_VSWITCH_CLIENTS} -I${SDDF}/include/sddf/util

CHECK_VSWITCH_FLAGS_MD5:=.vswitch_cflags-$(shell echo -- ${CFLAGS} ${CFLAGS_vswitch} | shasum | sed 's/ *-//')

${CHECK_VSWITCH_FLAGS_MD5}:
-rm -f .vswitch_cflags-*
touch $@


${VSWITCH_IMAGES}: LIBS := libsddf_util_debug.a ${LIBS}

${VSWITCH_COMPONENT_OBJ}: |network/components/vswitch
${VSWITCH_COMPONENT_OBJ}: ${CHECK_NETWORK_FLAGS_MD5}
${VSWITCH_COMPONENT_OBJ}: CFLAGS+=${CFLAGS_vswitch}

%.elf: network/components/vswitch/%.o
${LD} ${LDFLAGS} -o $@ $< ${LIBS}

clean::
rm -f vswitch.[od]

clobber::
rm -f ${IMAGES}

network/components/vswitch:
mkdir -p $@

-include ${VSWITCH_COMPONENTS_OBJS:.o=.d}
Loading
0