summaryrefslogtreecommitdiffstats
path: root/sys/contrib/rdma/rdma_addr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/contrib/rdma/rdma_addr.c')
-rw-r--r--sys/contrib/rdma/rdma_addr.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/sys/contrib/rdma/rdma_addr.c b/sys/contrib/rdma/rdma_addr.c
new file mode 100644
index 0000000..b3f7349
--- /dev/null
+++ b/sys/contrib/rdma/rdma_addr.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2005 Voltaire Inc. All rights reserved.
+ * Copyright (c) 2002-2005, Network Appliance, Inc. All rights reserved.
+ * Copyright (c) 1999-2005, Mellanox Technologies, Inc. All rights reserved.
+ * Copyright (c) 2005 Intel Corporation. All rights reserved.
+ *
+ * This Software is licensed under one of the following licenses:
+ *
+ * 1) under the terms of the "Common Public License 1.0" a copy of which is
+ * available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/cpl.php.
+ *
+ * 2) under the terms of the "The BSD License" a copy of which is
+ * available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/bsd-license.php.
+ *
+ * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
+ * copy of which is available from the Open Source Initiative, see
+ * http://www.opensource.org/licenses/gpl-license.php.
+ *
+ * Licensee has the right to choose one of the above licenses.
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice and one of the license notices.
+ *
+ * Redistributions in binary form must reproduce both the above copyright
+ * notice, one of the license notices in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/condvar.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/module.h>
+
+#include <sys/lock.h>
+#include <sys/condvar.h>
+#include <sys/mutex.h>
+#include <sys/rwlock.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/route.h>
+
+#include <net80211/ieee80211_freebsd.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <contrib/rdma/ib_addr.h>
+
+struct addr_req {
+ TAILQ_ENTRY(addr_req) entry;
+ struct sockaddr src_addr;
+ struct sockaddr dst_addr;
+ struct rdma_dev_addr *addr;
+ struct rdma_addr_client *client;
+ void *context;
+ void (*callback)(int status, struct sockaddr *src_addr,
+ struct rdma_dev_addr *addr, void *context);
+ unsigned long timeout;
+ int status;
+};
+
+static void process_req(void *ctx, int pending);
+
+static struct mtx lock;
+
+static TAILQ_HEAD(addr_req_list, addr_req) req_list;
+static struct task addr_task;
+static struct taskqueue *addr_taskq;
+static struct callout addr_ch;
+static eventhandler_tag route_event_tag;
+
+static void addr_timeout(void *arg)
+{
+ taskqueue_enqueue(addr_taskq, &addr_task);
+}
+
+void rdma_addr_register_client(struct rdma_addr_client *client)
+{
+ mtx_init(&client->lock, "rdma_addr client lock", NULL, MTX_DUPOK|MTX_DEF);
+ cv_init(&client->comp, "rdma_addr cv");
+ client->refcount = 1;
+}
+
+static inline void put_client(struct rdma_addr_client *client)
+{
+ mtx_lock(&client->lock);
+ if (--client->refcount == 0) {
+ cv_broadcast(&client->comp);
+ }
+ mtx_unlock(&client->lock);
+}
+
+void rdma_addr_unregister_client(struct rdma_addr_client *client)
+{
+ put_client(client);
+ mtx_lock(&client->lock);
+ if (client->refcount) {
+ cv_wait(&client->comp, &client->lock);
+ }
+ mtx_unlock(&client->lock);
+}
+
+int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct ifnet *dev,
+ const unsigned char *dst_dev_addr)
+{
+ dev_addr->dev_type = RDMA_NODE_RNIC;
+ memcpy(dev_addr->src_dev_addr, IF_LLADDR(dev), MAX_ADDR_LEN);
+ memcpy(dev_addr->broadcast, dev->if_broadcastaddr, MAX_ADDR_LEN);
+ if (dst_dev_addr)
+ memcpy(dev_addr->dst_dev_addr, dst_dev_addr, MAX_ADDR_LEN);
+ return 0;
+}
+
+int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
+{
+ struct ifaddr *ifa;
+ struct sockaddr_in *sin = (struct sockaddr_in *)addr;
+ uint16_t port = sin->sin_port;
+ int ret;
+
+ sin->sin_port = 0;
+ ifa = ifa_ifwithaddr(addr);
+ sin->sin_port = port;
+ if (!ifa)
+ return (EADDRNOTAVAIL);
+ ret = rdma_copy_addr(dev_addr, ifa->ifa_ifp, NULL);
+ ifa_free(ifa);
+ return (ret);
+}
+
+static void queue_req(struct addr_req *req)
+{
+ struct addr_req *tmp_req = NULL;
+
+ mtx_lock(&lock);
+ TAILQ_FOREACH_REVERSE(tmp_req, &req_list, addr_req_list, entry)
+ if (time_after_eq(req->timeout, tmp_req->timeout))
+ break;
+
+ if (tmp_req)
+ TAILQ_INSERT_AFTER(&req_list, tmp_req, req, entry);
+ else
+ TAILQ_INSERT_TAIL(&req_list, req, entry);
+
+ if (TAILQ_FIRST(&req_list) == req)
+ callout_reset(&addr_ch, req->timeout - ticks, addr_timeout, NULL);
+ mtx_unlock(&lock);
+}
+
+#ifdef needed
+static void addr_send_arp(struct sockaddr_in *dst_in)
+{
+ struct route iproute;
+ struct sockaddr_in *dst = (struct sockaddr_in *)&iproute.ro_dst;
+ char dmac[ETHER_ADDR_LEN];
+ struct llentry *lle;
+
+ bzero(&iproute, sizeof iproute);
+ *dst = *dst_in;
+
+ rtalloc(&iproute);
+ if (iproute.ro_rt == NULL);
+ return;
+
+ arpresolve(iproute.ro_rt->rt_ifp, iproute.ro_rt, NULL,
+ rt_key(iproute.ro_rt), dmac, &lle);
+
+ RTFREE(iproute.ro_rt);
+}
+#endif
+
+static int addr_resolve_remote(struct sockaddr_in *src_in,
+ struct sockaddr_in *dst_in,
+ struct rdma_dev_addr *addr)
+{
+ int ret = 0;
+ struct route iproute;
+ struct sockaddr_in *dst = (struct sockaddr_in *)&iproute.ro_dst;
+ char dmac[ETHER_ADDR_LEN];
+ struct llentry *lle;
+
+ bzero(&iproute, sizeof iproute);
+ *dst = *dst_in;
+
+ rtalloc(&iproute);
+ if (iproute.ro_rt == NULL) {
+ ret = EHOSTUNREACH;
+ goto out;
+ }
+
+ /* If the device does ARP internally, return 'done' */
+ if (iproute.ro_rt->rt_ifp->if_flags & IFF_NOARP) {
+ rdma_copy_addr(addr, iproute.ro_rt->rt_ifp, NULL);
+ goto put;
+ }
+ ret = arpresolve(iproute.ro_rt->rt_ifp, iproute.ro_rt, NULL,
+ rt_key(iproute.ro_rt), dmac, &lle);
+ if (ret) {
+ goto put;
+ }
+
+ if (!src_in->sin_addr.s_addr) {
+ src_in->sin_len = sizeof *src_in;
+ src_in->sin_family = dst_in->sin_family;
+ src_in->sin_addr.s_addr = ((struct sockaddr_in *)iproute.ro_rt->rt_ifa->ifa_addr)->sin_addr.s_addr;
+ }
+
+ ret = rdma_copy_addr(addr, iproute.ro_rt->rt_ifp, dmac);
+put:
+ RTFREE(iproute.ro_rt);
+out:
+ return ret;
+}
+
+static void process_req(void *ctx, int pending)
+{
+ struct addr_req *req, *tmp_req;
+ struct sockaddr_in *src_in, *dst_in;
+ TAILQ_HEAD(, addr_req) done_list;
+
+ TAILQ_INIT(&done_list);
+
+ mtx_lock(&lock);
+ TAILQ_FOREACH_SAFE(req, &req_list, entry, tmp_req) {
+ if (req->status == EWOULDBLOCK) {
+ src_in = (struct sockaddr_in *) &req->src_addr;
+ dst_in = (struct sockaddr_in *) &req->dst_addr;
+ req->status = addr_resolve_remote(src_in, dst_in,
+ req->addr);
+ if (req->status && time_after_eq(ticks, req->timeout))
+ req->status = ETIMEDOUT;
+ else if (req->status == EWOULDBLOCK)
+ continue;
+ }
+ TAILQ_REMOVE(&req_list, req, entry);
+ TAILQ_INSERT_TAIL(&done_list, req, entry);
+ }
+
+ if (!TAILQ_EMPTY(&req_list)) {
+ req = TAILQ_FIRST(&req_list);
+ callout_reset(&addr_ch, req->timeout - ticks, addr_timeout,
+ NULL);
+ }
+ mtx_unlock(&lock);
+
+ TAILQ_FOREACH_SAFE(req, &done_list, entry, tmp_req) {
+ TAILQ_REMOVE(&done_list, req, entry);
+ req->callback(req->status, &req->src_addr, req->addr,
+ req->context);
+ put_client(req->client);
+ free(req, M_DEVBUF);
+ }
+}
+
+int rdma_resolve_ip(struct rdma_addr_client *client,
+ struct sockaddr *src_addr, struct sockaddr *dst_addr,
+ struct rdma_dev_addr *addr, int timeout_ms,
+ void (*callback)(int status, struct sockaddr *src_addr,
+ struct rdma_dev_addr *addr, void *context),
+ void *context)
+{
+ struct sockaddr_in *src_in, *dst_in;
+ struct addr_req *req;
+ int ret = 0;
+
+ req = malloc(sizeof *req, M_DEVBUF, M_NOWAIT);
+ if (!req)
+ return (ENOMEM);
+ memset(req, 0, sizeof *req);
+
+ if (src_addr)
+ memcpy(&req->src_addr, src_addr, ip_addr_size(src_addr));
+ memcpy(&req->dst_addr, dst_addr, ip_addr_size(dst_addr));
+ req->addr = addr;
+ req->callback = callback;
+ req->context = context;
+ req->client = client;
+ mtx_lock(&client->lock);
+ client->refcount++;
+ mtx_unlock(&client->lock);
+
+ src_in = (struct sockaddr_in *) &req->src_addr;
+ dst_in = (struct sockaddr_in *) &req->dst_addr;
+
+ req->status = addr_resolve_remote(src_in, dst_in, addr);
+
+ switch (req->status) {
+ case 0:
+ req->timeout = ticks;
+ queue_req(req);
+ break;
+ case EWOULDBLOCK:
+ req->timeout = msecs_to_ticks(timeout_ms) + ticks;
+ queue_req(req);
+#ifdef needed
+ addr_send_arp(dst_in);
+#endif
+ break;
+ default:
+ ret = req->status;
+ mtx_lock(&client->lock);
+ client->refcount--;
+ mtx_unlock(&client->lock);
+ free(req, M_DEVBUF);
+ break;
+ }
+ return ret;
+}
+
+void rdma_addr_cancel(struct rdma_dev_addr *addr)
+{
+ struct addr_req *req, *tmp_req;
+
+ mtx_lock(&lock);
+ TAILQ_FOREACH_SAFE(req, &req_list, entry, tmp_req) {
+ if (req->addr == addr) {
+ req->status = ECANCELED;
+ req->timeout = ticks;
+ TAILQ_REMOVE(&req_list, req, entry);
+ TAILQ_INSERT_HEAD(&req_list, req, entry);
+ callout_reset(&addr_ch, req->timeout - ticks, addr_timeout, NULL);
+ break;
+ }
+ }
+ mtx_unlock(&lock);
+}
+
+static void
+route_event_arp_update(void *unused, struct rtentry *rt0, uint8_t *enaddr,
+ struct sockaddr *sa)
+{
+ callout_stop(&addr_ch);
+ taskqueue_enqueue(addr_taskq, &addr_task);
+}
+
+static int addr_init(void)
+{
+ TAILQ_INIT(&req_list);
+ mtx_init(&lock, "rdma_addr req_list lock", NULL, MTX_DEF);
+
+ addr_taskq = taskqueue_create("rdma_addr_taskq", M_NOWAIT,
+ taskqueue_thread_enqueue, &addr_taskq);
+ if (addr_taskq == NULL) {
+ printf("failed to allocate rdma_addr taskqueue\n");
+ return (ENOMEM);
+ }
+ taskqueue_start_threads(&addr_taskq, 1, PI_NET, "rdma_addr taskq");
+ TASK_INIT(&addr_task, 0, process_req, NULL);
+
+ callout_init(&addr_ch, TRUE);
+
+ route_event_tag = EVENTHANDLER_REGISTER(route_arp_update_event,
+ route_event_arp_update, NULL, EVENTHANDLER_PRI_ANY);
+
+ return 0;
+}
+
+static void addr_cleanup(void)
+{
+ EVENTHANDLER_DEREGISTER(route_event_arp_update, route_event_tag);
+ callout_stop(&addr_ch);
+ taskqueue_drain(addr_taskq, &addr_task);
+ taskqueue_free(addr_taskq);
+}
+
+static int
+addr_load(module_t mod, int cmd, void *arg)
+{
+ int err = 0;
+
+ switch (cmd) {
+ case MOD_LOAD:
+ printf("Loading rdma_addr.\n");
+
+ addr_init();
+ break;
+ case MOD_QUIESCE:
+ break;
+ case MOD_UNLOAD:
+ printf("Unloading rdma_addr.\n");
+ addr_cleanup();
+ break;
+ case MOD_SHUTDOWN:
+ break;
+ default:
+ err = EOPNOTSUPP;
+ break;
+ }
+
+ return (err);
+}
+
+static moduledata_t mod_data = {
+ "rdma_addr",
+ addr_load,
+ 0
+};
+
+MODULE_VERSION(rdma_addr, 1);
+DECLARE_MODULE(rdma_addr, mod_data, SI_SUB_EXEC, SI_ORDER_ANY);
OpenPOWER on IntegriCloud