mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-17 15:27:36 +00:00
Move CLIP table handling out of TOM and into the base driver.
- Store the clip table in 'struct adapter' instead of in the TOM softc. - Init the clip table during attach and teardown during detach. - While here, add a dev.<nexus>.<unit>.misc.clip sysctl to dump the CLIP table. This does mean that we update the clip table even if TOE is not enabled, but non-TOE things need the CLIP table anyway. Reviewed by: np, Krishnamraju Eraparaju @ Chelsio Sponsored by: Chelsio Communications Differential Revision: https://reviews.freebsd.org/D18010
This commit is contained in:
parent
468ed39612
commit
78afed1396
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=341172
@ -1382,6 +1382,8 @@ dev/cxgb/sys/uipc_mvec.c optional cxgb pci \
|
||||
compile-with "${NORMAL_C} -I$S/dev/cxgb"
|
||||
dev/cxgb/cxgb_t3fw.c optional cxgb cxgb_t3fw \
|
||||
compile-with "${NORMAL_C} -I$S/dev/cxgb"
|
||||
dev/cxgbe/t4_clip.c optional cxgbe pci \
|
||||
compile-with "${NORMAL_C} -I$S/dev/cxgbe"
|
||||
dev/cxgbe/t4_filter.c optional cxgbe pci \
|
||||
compile-with "${NORMAL_C} -I$S/dev/cxgbe"
|
||||
dev/cxgbe/t4_if.m optional cxgbe pci
|
||||
|
@ -771,6 +771,8 @@ struct devnames {
|
||||
const char *vf_ifnet_name;
|
||||
};
|
||||
|
||||
struct clip_entry;
|
||||
|
||||
struct adapter {
|
||||
SLIST_ENTRY(adapter) link;
|
||||
device_t dev;
|
||||
@ -817,6 +819,10 @@ struct adapter {
|
||||
struct port_info *port[MAX_NPORTS];
|
||||
uint8_t chan_map[MAX_NCHAN]; /* channel -> port */
|
||||
|
||||
struct mtx clip_table_lock;
|
||||
TAILQ_HEAD(, clip_entry) clip_table;
|
||||
int clip_gen;
|
||||
|
||||
void *tom_softc; /* (struct tom_data *) */
|
||||
struct tom_tunables tt;
|
||||
struct t4_offload_policy *policy;
|
||||
|
382
sys/dev/cxgbe/t4_clip.c
Normal file
382
sys/dev/cxgbe/t4_clip.c
Normal file
@ -0,0 +1,382 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2012 Chelsio Communications, Inc.
|
||||
* All rights reserved.
|
||||
* Written by: Navdeep Parhar <np@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ck.h>
|
||||
#include <sys/eventhandler.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/rmlock.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/taskqueue.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet6/in6_var.h>
|
||||
#include <netinet6/scope6_var.h>
|
||||
|
||||
#include "common/common.h"
|
||||
#include "t4_clip.h"
|
||||
|
||||
static int add_lip(struct adapter *, struct in6_addr *);
|
||||
static int delete_lip(struct adapter *, struct in6_addr *);
|
||||
static struct clip_entry *search_lip(struct adapter *, struct in6_addr *);
|
||||
static void update_clip(struct adapter *, void *);
|
||||
static void t4_clip_task(void *, int);
|
||||
static void update_clip_table(struct adapter *);
|
||||
|
||||
static int in6_ifaddr_gen;
|
||||
static eventhandler_tag ifaddr_evhandler;
|
||||
static struct timeout_task clip_task;
|
||||
|
||||
static int
|
||||
add_lip(struct adapter *sc, struct in6_addr *lip)
|
||||
{
|
||||
struct fw_clip_cmd c;
|
||||
|
||||
ASSERT_SYNCHRONIZED_OP(sc);
|
||||
mtx_assert(&sc->clip_table_lock, MA_OWNED);
|
||||
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
|
||||
F_FW_CMD_WRITE);
|
||||
c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c));
|
||||
c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
|
||||
c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
|
||||
|
||||
return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c));
|
||||
}
|
||||
|
||||
static int
|
||||
delete_lip(struct adapter *sc, struct in6_addr *lip)
|
||||
{
|
||||
struct fw_clip_cmd c;
|
||||
|
||||
ASSERT_SYNCHRONIZED_OP(sc);
|
||||
mtx_assert(&sc->clip_table_lock, MA_OWNED);
|
||||
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
|
||||
F_FW_CMD_READ);
|
||||
c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_FREE | FW_LEN16(c));
|
||||
c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
|
||||
c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
|
||||
|
||||
return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c));
|
||||
}
|
||||
|
||||
static struct clip_entry *
|
||||
search_lip(struct adapter *sc, struct in6_addr *lip)
|
||||
{
|
||||
struct clip_entry *ce;
|
||||
|
||||
mtx_assert(&sc->clip_table_lock, MA_OWNED);
|
||||
|
||||
TAILQ_FOREACH(ce, &sc->clip_table, link) {
|
||||
if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip))
|
||||
return (ce);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
struct clip_entry *
|
||||
t4_hold_lip(struct adapter *sc, struct in6_addr *lip, struct clip_entry *ce)
|
||||
{
|
||||
|
||||
mtx_lock(&sc->clip_table_lock);
|
||||
if (ce == NULL)
|
||||
ce = search_lip(sc, lip);
|
||||
if (ce != NULL)
|
||||
ce->refcount++;
|
||||
mtx_unlock(&sc->clip_table_lock);
|
||||
|
||||
return (ce);
|
||||
}
|
||||
|
||||
void
|
||||
t4_release_lip(struct adapter *sc, struct clip_entry *ce)
|
||||
{
|
||||
|
||||
mtx_lock(&sc->clip_table_lock);
|
||||
KASSERT(search_lip(sc, &ce->lip) == ce,
|
||||
("%s: CLIP entry %p p not in CLIP table.", __func__, ce));
|
||||
KASSERT(ce->refcount > 0,
|
||||
("%s: CLIP entry %p has refcount 0", __func__, ce));
|
||||
--ce->refcount;
|
||||
mtx_unlock(&sc->clip_table_lock);
|
||||
}
|
||||
|
||||
void
|
||||
t4_init_clip_table(struct adapter *sc)
|
||||
{
|
||||
|
||||
mtx_init(&sc->clip_table_lock, "CLIP table lock", NULL, MTX_DEF);
|
||||
TAILQ_INIT(&sc->clip_table);
|
||||
sc->clip_gen = -1;
|
||||
|
||||
/*
|
||||
* Don't bother forcing an update of the clip table when the
|
||||
* adapter is initialized. Before an interface can be used it
|
||||
* must be assigned an address which will trigger the event
|
||||
* handler to update the table.
|
||||
*/
|
||||
}
|
||||
|
||||
static void
|
||||
update_clip(struct adapter *sc, void *arg __unused)
|
||||
{
|
||||
|
||||
if (begin_synchronized_op(sc, NULL, HOLD_LOCK, "t4clip"))
|
||||
return;
|
||||
|
||||
if (mtx_initialized(&sc->clip_table_lock))
|
||||
update_clip_table(sc);
|
||||
|
||||
end_synchronized_op(sc, LOCK_HELD);
|
||||
}
|
||||
|
||||
static void
|
||||
t4_clip_task(void *arg, int count)
|
||||
{
|
||||
|
||||
t4_iterate(update_clip, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
update_clip_table(struct adapter *sc)
|
||||
{
|
||||
struct rm_priotracker in6_ifa_tracker;
|
||||
struct in6_ifaddr *ia;
|
||||
struct in6_addr *lip, tlip;
|
||||
TAILQ_HEAD(, clip_entry) stale;
|
||||
struct clip_entry *ce, *ce_temp;
|
||||
struct vi_info *vi;
|
||||
int rc, gen, i, j;
|
||||
uintptr_t last_vnet;
|
||||
|
||||
ASSERT_SYNCHRONIZED_OP(sc);
|
||||
|
||||
IN6_IFADDR_RLOCK(&in6_ifa_tracker);
|
||||
mtx_lock(&sc->clip_table_lock);
|
||||
|
||||
gen = atomic_load_acq_int(&in6_ifaddr_gen);
|
||||
if (gen == sc->clip_gen)
|
||||
goto done;
|
||||
|
||||
TAILQ_INIT(&stale);
|
||||
TAILQ_CONCAT(&stale, &sc->clip_table, link);
|
||||
|
||||
/*
|
||||
* last_vnet optimizes the common cases where all if_vnet = NULL (no
|
||||
* VIMAGE) or all if_vnet = vnet0.
|
||||
*/
|
||||
last_vnet = (uintptr_t)(-1);
|
||||
for_each_port(sc, i)
|
||||
for_each_vi(sc->port[i], j, vi) {
|
||||
if (last_vnet == (uintptr_t)vi->ifp->if_vnet)
|
||||
continue;
|
||||
|
||||
/* XXX: races with if_vmove */
|
||||
CURVNET_SET(vi->ifp->if_vnet);
|
||||
CK_STAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
|
||||
lip = &ia->ia_addr.sin6_addr;
|
||||
|
||||
KASSERT(!IN6_IS_ADDR_MULTICAST(lip),
|
||||
("%s: mcast address in in6_ifaddr list", __func__));
|
||||
|
||||
if (IN6_IS_ADDR_LOOPBACK(lip))
|
||||
continue;
|
||||
if (IN6_IS_SCOPE_EMBED(lip)) {
|
||||
/* Remove the embedded scope */
|
||||
tlip = *lip;
|
||||
lip = &tlip;
|
||||
in6_clearscope(lip);
|
||||
}
|
||||
/*
|
||||
* XXX: how to weed out the link local address for the
|
||||
* loopback interface? It's fe80::1 usually (always?).
|
||||
*/
|
||||
|
||||
/*
|
||||
* If it's in the main list then we already know it's
|
||||
* not stale.
|
||||
*/
|
||||
TAILQ_FOREACH(ce, &sc->clip_table, link) {
|
||||
if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip))
|
||||
goto next;
|
||||
}
|
||||
|
||||
/*
|
||||
* If it's in the stale list we should move it to the
|
||||
* main list.
|
||||
*/
|
||||
TAILQ_FOREACH(ce, &stale, link) {
|
||||
if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip)) {
|
||||
TAILQ_REMOVE(&stale, ce, link);
|
||||
TAILQ_INSERT_TAIL(&sc->clip_table, ce,
|
||||
link);
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
/* A new IP6 address; add it to the CLIP table */
|
||||
ce = malloc(sizeof(*ce), M_CXGBE, M_NOWAIT);
|
||||
memcpy(&ce->lip, lip, sizeof(ce->lip));
|
||||
ce->refcount = 0;
|
||||
rc = add_lip(sc, lip);
|
||||
if (rc == 0)
|
||||
TAILQ_INSERT_TAIL(&sc->clip_table, ce, link);
|
||||
else {
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET6, &ce->lip, &ip[0],
|
||||
sizeof(ip));
|
||||
log(LOG_ERR, "%s: could not add %s (%d)\n",
|
||||
__func__, ip, rc);
|
||||
free(ce, M_CXGBE);
|
||||
}
|
||||
next:
|
||||
continue;
|
||||
}
|
||||
CURVNET_RESTORE();
|
||||
last_vnet = (uintptr_t)vi->ifp->if_vnet;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove stale addresses (those no longer in V_in6_ifaddrhead) that are
|
||||
* no longer referenced by the driver.
|
||||
*/
|
||||
TAILQ_FOREACH_SAFE(ce, &stale, link, ce_temp) {
|
||||
if (ce->refcount == 0) {
|
||||
rc = delete_lip(sc, &ce->lip);
|
||||
if (rc == 0) {
|
||||
TAILQ_REMOVE(&stale, ce, link);
|
||||
free(ce, M_CXGBE);
|
||||
} else {
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET6, &ce->lip, &ip[0],
|
||||
sizeof(ip));
|
||||
log(LOG_ERR, "%s: could not delete %s (%d)\n",
|
||||
__func__, ip, rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* The ones that are still referenced need to stay in the CLIP table */
|
||||
TAILQ_CONCAT(&sc->clip_table, &stale, link);
|
||||
|
||||
sc->clip_gen = gen;
|
||||
done:
|
||||
mtx_unlock(&sc->clip_table_lock);
|
||||
IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
|
||||
}
|
||||
|
||||
void
|
||||
t4_destroy_clip_table(struct adapter *sc)
|
||||
{
|
||||
struct clip_entry *ce, *ce_temp;
|
||||
|
||||
if (mtx_initialized(&sc->clip_table_lock)) {
|
||||
mtx_lock(&sc->clip_table_lock);
|
||||
TAILQ_FOREACH_SAFE(ce, &sc->clip_table, link, ce_temp) {
|
||||
KASSERT(ce->refcount == 0,
|
||||
("%s: CLIP entry %p still in use (%d)", __func__,
|
||||
ce, ce->refcount));
|
||||
TAILQ_REMOVE(&sc->clip_table, ce, link);
|
||||
delete_lip(sc, &ce->lip);
|
||||
free(ce, M_CXGBE);
|
||||
}
|
||||
mtx_unlock(&sc->clip_table_lock);
|
||||
mtx_destroy(&sc->clip_table_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
t4_tom_ifaddr_event(void *arg __unused, struct ifnet *ifp)
|
||||
{
|
||||
|
||||
atomic_add_rel_int(&in6_ifaddr_gen, 1);
|
||||
taskqueue_enqueue_timeout(taskqueue_thread, &clip_task, -hz / 4);
|
||||
}
|
||||
|
||||
int
|
||||
sysctl_clip(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct adapter *sc = arg1;
|
||||
struct clip_entry *ce;
|
||||
struct sbuf *sb;
|
||||
int rc, header = 0;
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
|
||||
rc = sysctl_wire_old_buffer(req, 0);
|
||||
if (rc != 0)
|
||||
return (rc);
|
||||
|
||||
sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
|
||||
if (sb == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
mtx_lock(&sc->clip_table_lock);
|
||||
TAILQ_FOREACH(ce, &sc->clip_table, link) {
|
||||
if (header == 0) {
|
||||
sbuf_printf(sb, "%-40s %-5s", "IP address", "Users");
|
||||
header = 1;
|
||||
}
|
||||
inet_ntop(AF_INET6, &ce->lip, &ip[0], sizeof(ip));
|
||||
|
||||
sbuf_printf(sb, "\n%-40s %5u", ip, ce->refcount);
|
||||
}
|
||||
mtx_unlock(&sc->clip_table_lock);
|
||||
|
||||
rc = sbuf_finish(sb);
|
||||
sbuf_delete(sb);
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
void
|
||||
t4_clip_modload(void)
|
||||
{
|
||||
|
||||
TIMEOUT_TASK_INIT(taskqueue_thread, &clip_task, 0, t4_clip_task, NULL);
|
||||
ifaddr_evhandler = EVENTHANDLER_REGISTER(ifaddr_event,
|
||||
t4_tom_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY);
|
||||
}
|
||||
|
||||
void
|
||||
t4_clip_modunload(void)
|
||||
{
|
||||
|
||||
EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_evhandler);
|
||||
taskqueue_cancel_timeout(taskqueue_thread, &clip_task, NULL);
|
||||
}
|
51
sys/dev/cxgbe/t4_clip.h
Normal file
51
sys/dev/cxgbe/t4_clip.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2012 Chelsio Communications, Inc.
|
||||
* All rights reserved.
|
||||
* Written by: Navdeep Parhar <np@FreeBSD.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __T4_CLIP_H
|
||||
#define __T4_CLIP_H
|
||||
|
||||
struct clip_entry {
|
||||
TAILQ_ENTRY(clip_entry) link;
|
||||
struct in6_addr lip; /* local IPv6 address */
|
||||
u_int refcount;
|
||||
};
|
||||
|
||||
void t4_clip_modload(void);
|
||||
void t4_clip_modunload(void);
|
||||
void t4_init_clip_table(struct adapter *);
|
||||
void t4_destroy_clip_table(struct adapter *);
|
||||
struct clip_entry *t4_hold_lip(struct adapter *, struct in6_addr *,
|
||||
struct clip_entry *);
|
||||
void t4_release_lip(struct adapter *, struct clip_entry *);
|
||||
|
||||
int sysctl_clip(SYSCTL_HANDLER_ARGS);
|
||||
|
||||
#endif /* __T4_CLIP_H */
|
@ -82,6 +82,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "common/t4_regs.h"
|
||||
#include "common/t4_regs_values.h"
|
||||
#include "cudbg/cudbg.h"
|
||||
#include "t4_clip.h"
|
||||
#include "t4_ioctl.h"
|
||||
#include "t4_l2t.h"
|
||||
#include "t4_mp_ring.h"
|
||||
@ -1221,6 +1222,7 @@ t4_attach(device_t dev)
|
||||
#ifdef RATELIMIT
|
||||
t4_init_etid_table(sc);
|
||||
#endif
|
||||
t4_init_clip_table(sc);
|
||||
if (sc->vres.key.size != 0)
|
||||
sc->key_map = vmem_create("T4TLS key map", sc->vres.key.start,
|
||||
sc->vres.key.size, 32, 0, M_FIRSTFIT | M_WAITOK);
|
||||
@ -1511,6 +1513,7 @@ t4_detach_common(device_t dev)
|
||||
#endif
|
||||
if (sc->key_map)
|
||||
vmem_destroy(sc->key_map);
|
||||
t4_destroy_clip_table(sc);
|
||||
|
||||
#if defined(TCP_OFFLOAD) || defined(RATELIMIT)
|
||||
free(sc->sge.ofld_txq, M_CXGBE);
|
||||
@ -5964,6 +5967,10 @@ t4_sysctls(struct adapter *sc)
|
||||
CTLTYPE_STRING | CTLFLAG_RD, sc, 0,
|
||||
sysctl_smt, "A", "hardware source MAC table");
|
||||
|
||||
SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "clip",
|
||||
CTLTYPE_STRING | CTLFLAG_RD, sc, 0,
|
||||
sysctl_clip, "A", "active CLIP table entries");
|
||||
|
||||
SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "lb_stats",
|
||||
CTLTYPE_STRING | CTLFLAG_RD, sc, 0,
|
||||
sysctl_lb_stats, "A", "loopback statistics");
|
||||
@ -10520,6 +10527,7 @@ mod_event(module_t mod, int cmd, void *arg)
|
||||
sx_init(&t4_uld_list_lock, "T4/T5 ULDs");
|
||||
SLIST_INIT(&t4_uld_list);
|
||||
#endif
|
||||
t4_clip_modload();
|
||||
t4_tracer_modload();
|
||||
tweak_tunables();
|
||||
}
|
||||
@ -10559,6 +10567,7 @@ mod_event(module_t mod, int cmd, void *arg)
|
||||
|
||||
if (t4_sge_extfree_refs() == 0) {
|
||||
t4_tracer_modunload();
|
||||
t4_clip_modunload();
|
||||
#ifdef TCP_OFFLOAD
|
||||
sx_destroy(&t4_uld_list_lock);
|
||||
#endif
|
||||
|
@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "common/t4_msg.h"
|
||||
#include "common/t4_regs.h"
|
||||
#include "common/t4_regs_values.h"
|
||||
#include "t4_clip.h"
|
||||
#include "tom/t4_tom_l2t.h"
|
||||
#include "tom/t4_tom.h"
|
||||
|
||||
@ -316,7 +317,6 @@ t4_connect(struct toedev *tod, struct socket *so, struct rtentry *rt,
|
||||
struct sockaddr *nam)
|
||||
{
|
||||
struct adapter *sc = tod->tod_softc;
|
||||
struct tom_data *td = tod_td(tod);
|
||||
struct toepcb *toep = NULL;
|
||||
struct wrqe *wr = NULL;
|
||||
struct ifnet *rt_ifp = rt->rt_ifp;
|
||||
@ -409,7 +409,7 @@ t4_connect(struct toedev *tod, struct socket *so, struct rtentry *rt,
|
||||
if ((inp->inp_vflag & INP_IPV6) == 0)
|
||||
DONT_OFFLOAD_ACTIVE_OPEN(ENOTSUP);
|
||||
|
||||
toep->ce = hold_lip(td, &inp->in6p_laddr, NULL);
|
||||
toep->ce = t4_hold_lip(sc, &inp->in6p_laddr, NULL);
|
||||
if (toep->ce == NULL)
|
||||
DONT_OFFLOAD_ACTIVE_OPEN(ENOENT);
|
||||
|
||||
@ -496,7 +496,7 @@ t4_connect(struct toedev *tod, struct socket *so, struct rtentry *rt,
|
||||
if (toep->l2te)
|
||||
t4_l2t_release(toep->l2te);
|
||||
if (toep->ce)
|
||||
release_lip(td, toep->ce);
|
||||
t4_release_lip(sc, toep->ce);
|
||||
free_toepcb(toep);
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "common/common.h"
|
||||
#include "common/t4_msg.h"
|
||||
#include "common/t4_regs.h"
|
||||
#include "t4_clip.h"
|
||||
#include "tom/t4_tom_l2t.h"
|
||||
#include "tom/t4_tom.h"
|
||||
|
||||
@ -212,9 +213,7 @@ alloc_lctx(struct adapter *sc, struct inpcb *inp, struct vi_info *vi)
|
||||
|
||||
if (inp->inp_vflag & INP_IPV6 &&
|
||||
!IN6_ARE_ADDR_EQUAL(&in6addr_any, &inp->in6p_laddr)) {
|
||||
struct tom_data *td = sc->tom_softc;
|
||||
|
||||
lctx->ce = hold_lip(td, &inp->in6p_laddr, NULL);
|
||||
lctx->ce = t4_hold_lip(sc, &inp->in6p_laddr, NULL);
|
||||
if (lctx->ce == NULL) {
|
||||
free(lctx, M_CXGBE);
|
||||
return (NULL);
|
||||
@ -238,7 +237,6 @@ static int
|
||||
free_lctx(struct adapter *sc, struct listen_ctx *lctx)
|
||||
{
|
||||
struct inpcb *inp = lctx->inp;
|
||||
struct tom_data *td = sc->tom_softc;
|
||||
|
||||
INP_WLOCK_ASSERT(inp);
|
||||
KASSERT(lctx->refcount == 0,
|
||||
@ -251,7 +249,7 @@ free_lctx(struct adapter *sc, struct listen_ctx *lctx)
|
||||
__func__, lctx->stid, lctx, lctx->inp);
|
||||
|
||||
if (lctx->ce)
|
||||
release_lip(td, lctx->ce);
|
||||
t4_release_lip(sc, lctx->ce);
|
||||
free_stid(sc, lctx);
|
||||
free(lctx, M_CXGBE);
|
||||
|
||||
@ -1675,7 +1673,7 @@ do_pass_establish(struct sge_iq *iq, const struct rss_header *rss,
|
||||
MPASS(so->so_vnet == lctx->vnet);
|
||||
toep->vnet = lctx->vnet;
|
||||
if (inc.inc_flags & INC_ISIPV6)
|
||||
toep->ce = hold_lip(sc->tom_softc, &inc.inc6_laddr, lctx->ce);
|
||||
toep->ce = t4_hold_lip(sc, &inc.inc6_laddr, lctx->ce);
|
||||
|
||||
/*
|
||||
* This is for the unlikely case where the syncache entry that we added
|
||||
|
@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "common/t4_regs.h"
|
||||
#include "common/t4_regs_values.h"
|
||||
#include "common/t4_tcb.h"
|
||||
#include "t4_clip.h"
|
||||
#include "tom/t4_tom_l2t.h"
|
||||
#include "tom/t4_tom.h"
|
||||
#include "tom/t4_tls.h"
|
||||
@ -99,21 +100,9 @@ static struct uld_info tom_uld_info = {
|
||||
static void release_offload_resources(struct toepcb *);
|
||||
static int alloc_tid_tabs(struct tid_info *);
|
||||
static void free_tid_tabs(struct tid_info *);
|
||||
static int add_lip(struct adapter *, struct in6_addr *);
|
||||
static int delete_lip(struct adapter *, struct in6_addr *);
|
||||
static struct clip_entry *search_lip(struct tom_data *, struct in6_addr *);
|
||||
static void init_clip_table(struct adapter *, struct tom_data *);
|
||||
static void update_clip(struct adapter *, void *);
|
||||
static void t4_clip_task(void *, int);
|
||||
static void update_clip_table(struct adapter *, struct tom_data *);
|
||||
static void destroy_clip_table(struct adapter *, struct tom_data *);
|
||||
static void free_tom_data(struct adapter *, struct tom_data *);
|
||||
static void reclaim_wr_resources(void *, int);
|
||||
|
||||
static int in6_ifaddr_gen;
|
||||
static eventhandler_tag ifaddr_evhandler;
|
||||
static struct timeout_task clip_task;
|
||||
|
||||
struct toepcb *
|
||||
alloc_toepcb(struct vi_info *vi, int txqid, int rxqid, int flags)
|
||||
{
|
||||
@ -315,7 +304,7 @@ release_offload_resources(struct toepcb *toep)
|
||||
}
|
||||
|
||||
if (toep->ce)
|
||||
release_lip(td, toep->ce);
|
||||
t4_release_lip(sc, toep->ce);
|
||||
|
||||
if (toep->tc_idx != -1)
|
||||
t4_release_cl_rl(sc, toep->vi->pi->port_id, toep->tc_idx);
|
||||
@ -822,266 +811,6 @@ alloc_tid_tabs(struct tid_info *t)
|
||||
return (rc);
|
||||
}
|
||||
|
||||
static int
|
||||
add_lip(struct adapter *sc, struct in6_addr *lip)
|
||||
{
|
||||
struct fw_clip_cmd c;
|
||||
|
||||
ASSERT_SYNCHRONIZED_OP(sc);
|
||||
/* mtx_assert(&td->clip_table_lock, MA_OWNED); */
|
||||
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
|
||||
F_FW_CMD_WRITE);
|
||||
c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_ALLOC | FW_LEN16(c));
|
||||
c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
|
||||
c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
|
||||
|
||||
return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c));
|
||||
}
|
||||
|
||||
static int
|
||||
delete_lip(struct adapter *sc, struct in6_addr *lip)
|
||||
{
|
||||
struct fw_clip_cmd c;
|
||||
|
||||
ASSERT_SYNCHRONIZED_OP(sc);
|
||||
/* mtx_assert(&td->clip_table_lock, MA_OWNED); */
|
||||
|
||||
memset(&c, 0, sizeof(c));
|
||||
c.op_to_write = htonl(V_FW_CMD_OP(FW_CLIP_CMD) | F_FW_CMD_REQUEST |
|
||||
F_FW_CMD_READ);
|
||||
c.alloc_to_len16 = htonl(F_FW_CLIP_CMD_FREE | FW_LEN16(c));
|
||||
c.ip_hi = *(uint64_t *)&lip->s6_addr[0];
|
||||
c.ip_lo = *(uint64_t *)&lip->s6_addr[8];
|
||||
|
||||
return (-t4_wr_mbox_ns(sc, sc->mbox, &c, sizeof(c), &c));
|
||||
}
|
||||
|
||||
static struct clip_entry *
|
||||
search_lip(struct tom_data *td, struct in6_addr *lip)
|
||||
{
|
||||
struct clip_entry *ce;
|
||||
|
||||
mtx_assert(&td->clip_table_lock, MA_OWNED);
|
||||
|
||||
TAILQ_FOREACH(ce, &td->clip_table, link) {
|
||||
if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip))
|
||||
return (ce);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
struct clip_entry *
|
||||
hold_lip(struct tom_data *td, struct in6_addr *lip, struct clip_entry *ce)
|
||||
{
|
||||
|
||||
mtx_lock(&td->clip_table_lock);
|
||||
if (ce == NULL)
|
||||
ce = search_lip(td, lip);
|
||||
if (ce != NULL)
|
||||
ce->refcount++;
|
||||
mtx_unlock(&td->clip_table_lock);
|
||||
|
||||
return (ce);
|
||||
}
|
||||
|
||||
void
|
||||
release_lip(struct tom_data *td, struct clip_entry *ce)
|
||||
{
|
||||
|
||||
mtx_lock(&td->clip_table_lock);
|
||||
KASSERT(search_lip(td, &ce->lip) == ce,
|
||||
("%s: CLIP entry %p p not in CLIP table.", __func__, ce));
|
||||
KASSERT(ce->refcount > 0,
|
||||
("%s: CLIP entry %p has refcount 0", __func__, ce));
|
||||
--ce->refcount;
|
||||
mtx_unlock(&td->clip_table_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
init_clip_table(struct adapter *sc, struct tom_data *td)
|
||||
{
|
||||
|
||||
ASSERT_SYNCHRONIZED_OP(sc);
|
||||
|
||||
mtx_init(&td->clip_table_lock, "CLIP table lock", NULL, MTX_DEF);
|
||||
TAILQ_INIT(&td->clip_table);
|
||||
td->clip_gen = -1;
|
||||
|
||||
update_clip_table(sc, td);
|
||||
}
|
||||
|
||||
static void
|
||||
update_clip(struct adapter *sc, void *arg __unused)
|
||||
{
|
||||
|
||||
if (begin_synchronized_op(sc, NULL, HOLD_LOCK, "t4tomuc"))
|
||||
return;
|
||||
|
||||
if (uld_active(sc, ULD_TOM))
|
||||
update_clip_table(sc, sc->tom_softc);
|
||||
|
||||
end_synchronized_op(sc, LOCK_HELD);
|
||||
}
|
||||
|
||||
static void
|
||||
t4_clip_task(void *arg, int count)
|
||||
{
|
||||
|
||||
t4_iterate(update_clip, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
update_clip_table(struct adapter *sc, struct tom_data *td)
|
||||
{
|
||||
struct rm_priotracker in6_ifa_tracker;
|
||||
struct in6_ifaddr *ia;
|
||||
struct in6_addr *lip, tlip;
|
||||
struct clip_head stale;
|
||||
struct clip_entry *ce, *ce_temp;
|
||||
struct vi_info *vi;
|
||||
int rc, gen, i, j;
|
||||
uintptr_t last_vnet;
|
||||
|
||||
ASSERT_SYNCHRONIZED_OP(sc);
|
||||
|
||||
IN6_IFADDR_RLOCK(&in6_ifa_tracker);
|
||||
mtx_lock(&td->clip_table_lock);
|
||||
|
||||
gen = atomic_load_acq_int(&in6_ifaddr_gen);
|
||||
if (gen == td->clip_gen)
|
||||
goto done;
|
||||
|
||||
TAILQ_INIT(&stale);
|
||||
TAILQ_CONCAT(&stale, &td->clip_table, link);
|
||||
|
||||
/*
|
||||
* last_vnet optimizes the common cases where all if_vnet = NULL (no
|
||||
* VIMAGE) or all if_vnet = vnet0.
|
||||
*/
|
||||
last_vnet = (uintptr_t)(-1);
|
||||
for_each_port(sc, i)
|
||||
for_each_vi(sc->port[i], j, vi) {
|
||||
if (last_vnet == (uintptr_t)vi->ifp->if_vnet)
|
||||
continue;
|
||||
|
||||
/* XXX: races with if_vmove */
|
||||
CURVNET_SET(vi->ifp->if_vnet);
|
||||
CK_STAILQ_FOREACH(ia, &V_in6_ifaddrhead, ia_link) {
|
||||
lip = &ia->ia_addr.sin6_addr;
|
||||
|
||||
KASSERT(!IN6_IS_ADDR_MULTICAST(lip),
|
||||
("%s: mcast address in in6_ifaddr list", __func__));
|
||||
|
||||
if (IN6_IS_ADDR_LOOPBACK(lip))
|
||||
continue;
|
||||
if (IN6_IS_SCOPE_EMBED(lip)) {
|
||||
/* Remove the embedded scope */
|
||||
tlip = *lip;
|
||||
lip = &tlip;
|
||||
in6_clearscope(lip);
|
||||
}
|
||||
/*
|
||||
* XXX: how to weed out the link local address for the
|
||||
* loopback interface? It's fe80::1 usually (always?).
|
||||
*/
|
||||
|
||||
/*
|
||||
* If it's in the main list then we already know it's
|
||||
* not stale.
|
||||
*/
|
||||
TAILQ_FOREACH(ce, &td->clip_table, link) {
|
||||
if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip))
|
||||
goto next;
|
||||
}
|
||||
|
||||
/*
|
||||
* If it's in the stale list we should move it to the
|
||||
* main list.
|
||||
*/
|
||||
TAILQ_FOREACH(ce, &stale, link) {
|
||||
if (IN6_ARE_ADDR_EQUAL(&ce->lip, lip)) {
|
||||
TAILQ_REMOVE(&stale, ce, link);
|
||||
TAILQ_INSERT_TAIL(&td->clip_table, ce,
|
||||
link);
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
/* A new IP6 address; add it to the CLIP table */
|
||||
ce = malloc(sizeof(*ce), M_CXGBE, M_NOWAIT);
|
||||
memcpy(&ce->lip, lip, sizeof(ce->lip));
|
||||
ce->refcount = 0;
|
||||
rc = add_lip(sc, lip);
|
||||
if (rc == 0)
|
||||
TAILQ_INSERT_TAIL(&td->clip_table, ce, link);
|
||||
else {
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET6, &ce->lip, &ip[0],
|
||||
sizeof(ip));
|
||||
log(LOG_ERR, "%s: could not add %s (%d)\n",
|
||||
__func__, ip, rc);
|
||||
free(ce, M_CXGBE);
|
||||
}
|
||||
next:
|
||||
continue;
|
||||
}
|
||||
CURVNET_RESTORE();
|
||||
last_vnet = (uintptr_t)vi->ifp->if_vnet;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove stale addresses (those no longer in V_in6_ifaddrhead) that are
|
||||
* no longer referenced by the driver.
|
||||
*/
|
||||
TAILQ_FOREACH_SAFE(ce, &stale, link, ce_temp) {
|
||||
if (ce->refcount == 0) {
|
||||
rc = delete_lip(sc, &ce->lip);
|
||||
if (rc == 0) {
|
||||
TAILQ_REMOVE(&stale, ce, link);
|
||||
free(ce, M_CXGBE);
|
||||
} else {
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET6, &ce->lip, &ip[0],
|
||||
sizeof(ip));
|
||||
log(LOG_ERR, "%s: could not delete %s (%d)\n",
|
||||
__func__, ip, rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* The ones that are still referenced need to stay in the CLIP table */
|
||||
TAILQ_CONCAT(&td->clip_table, &stale, link);
|
||||
|
||||
td->clip_gen = gen;
|
||||
done:
|
||||
mtx_unlock(&td->clip_table_lock);
|
||||
IN6_IFADDR_RUNLOCK(&in6_ifa_tracker);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_clip_table(struct adapter *sc, struct tom_data *td)
|
||||
{
|
||||
struct clip_entry *ce, *ce_temp;
|
||||
|
||||
if (mtx_initialized(&td->clip_table_lock)) {
|
||||
mtx_lock(&td->clip_table_lock);
|
||||
TAILQ_FOREACH_SAFE(ce, &td->clip_table, link, ce_temp) {
|
||||
KASSERT(ce->refcount == 0,
|
||||
("%s: CLIP entry %p still in use (%d)", __func__,
|
||||
ce, ce->refcount));
|
||||
TAILQ_REMOVE(&td->clip_table, ce, link);
|
||||
delete_lip(sc, &ce->lip);
|
||||
free(ce, M_CXGBE);
|
||||
}
|
||||
mtx_unlock(&td->clip_table_lock);
|
||||
mtx_destroy(&td->clip_table_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
free_tom_data(struct adapter *sc, struct tom_data *td)
|
||||
{
|
||||
@ -1094,7 +823,6 @@ free_tom_data(struct adapter *sc, struct tom_data *td)
|
||||
("%s: lctx hash table is not empty.", __func__));
|
||||
|
||||
t4_free_ppod_region(&td->pr);
|
||||
destroy_clip_table(sc, td);
|
||||
|
||||
if (td->listen_mask != 0)
|
||||
hashdestroy(td->listen_hash, M_CXGBE, td->listen_mask);
|
||||
@ -1369,9 +1097,6 @@ t4_tom_activate(struct adapter *sc)
|
||||
t4_set_reg_field(sc, A_ULP_RX_TDDP_TAGMASK,
|
||||
V_TDDPTAGMASK(M_TDDPTAGMASK), td->pr.pr_tag_mask);
|
||||
|
||||
/* CLIP table for IPv6 offload */
|
||||
init_clip_table(sc, td);
|
||||
|
||||
/* toedev ops */
|
||||
tod = &td->tod;
|
||||
init_toedev(tod);
|
||||
@ -1449,14 +1174,6 @@ t4_tom_deactivate(struct adapter *sc)
|
||||
return (rc);
|
||||
}
|
||||
|
||||
static void
|
||||
t4_tom_ifaddr_event(void *arg __unused, struct ifnet *ifp)
|
||||
{
|
||||
|
||||
atomic_add_rel_int(&in6_ifaddr_gen, 1);
|
||||
taskqueue_enqueue_timeout(taskqueue_thread, &clip_task, -hz / 4);
|
||||
}
|
||||
|
||||
static int
|
||||
t4_aio_queue_tom(struct socket *so, struct kaiocb *job)
|
||||
{
|
||||
@ -1524,10 +1241,6 @@ t4_tom_mod_load(void)
|
||||
toe6_protosw.pr_ctloutput = t4_ctloutput_tom;
|
||||
toe6_protosw.pr_usrreqs = &toe6_usrreqs;
|
||||
|
||||
TIMEOUT_TASK_INIT(taskqueue_thread, &clip_task, 0, t4_clip_task, NULL);
|
||||
ifaddr_evhandler = EVENTHANDLER_REGISTER(ifaddr_event,
|
||||
t4_tom_ifaddr_event, NULL, EVENTHANDLER_PRI_ANY);
|
||||
|
||||
return (t4_register_uld(&tom_uld_info));
|
||||
}
|
||||
|
||||
@ -1552,11 +1265,6 @@ t4_tom_mod_unload(void)
|
||||
if (t4_unregister_uld(&tom_uld_info) == EBUSY)
|
||||
return (EBUSY);
|
||||
|
||||
if (ifaddr_evhandler) {
|
||||
EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_evhandler);
|
||||
taskqueue_cancel_timeout(taskqueue_thread, &clip_task, NULL);
|
||||
}
|
||||
|
||||
t4_tls_mod_unload();
|
||||
t4_ddp_mod_unload();
|
||||
|
||||
|
@ -259,13 +259,6 @@ struct listen_ctx {
|
||||
TAILQ_HEAD(, synq_entry) synq;
|
||||
};
|
||||
|
||||
struct clip_entry {
|
||||
TAILQ_ENTRY(clip_entry) link;
|
||||
struct in6_addr lip; /* local IPv6 address */
|
||||
u_int refcount;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(clip_head, clip_entry);
|
||||
struct tom_data {
|
||||
struct toedev tod;
|
||||
|
||||
@ -280,10 +273,6 @@ struct tom_data {
|
||||
|
||||
struct ppod_region pr;
|
||||
|
||||
struct mtx clip_table_lock;
|
||||
struct clip_head clip_table;
|
||||
int clip_gen;
|
||||
|
||||
/* WRs that will not be sent to the chip because L2 resolution failed */
|
||||
struct mtx unsent_wr_lock;
|
||||
STAILQ_HEAD(, wrqe) unsent_wr_list;
|
||||
@ -342,9 +331,6 @@ int select_ulp_mode(struct socket *, struct adapter *,
|
||||
struct offload_settings *);
|
||||
void set_ulp_mode(struct toepcb *, int);
|
||||
int negative_advice(int);
|
||||
struct clip_entry *hold_lip(struct tom_data *, struct in6_addr *,
|
||||
struct clip_entry *);
|
||||
void release_lip(struct tom_data *, struct clip_entry *);
|
||||
|
||||
/* t4_connect.c */
|
||||
void t4_init_connect_cpl_handlers(void);
|
||||
|
@ -15,6 +15,7 @@ SRCS+= opt_ofed.h
|
||||
SRCS+= opt_ratelimit.h
|
||||
SRCS+= opt_rss.h
|
||||
SRCS+= pci_if.h pci_iov_if.h
|
||||
SRCS+= t4_clip.c
|
||||
SRCS+= t4_filter.c
|
||||
SRCS+= t4_hw.c
|
||||
SRCS+= t4_if.c t4_if.h
|
||||
|
Loading…
Reference in New Issue
Block a user