]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/contrib/rdma/rdma_addr.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / contrib / rdma / rdma_addr.c
1 /*
2  * Copyright (c) 2005 Voltaire Inc.  All rights reserved.
3  * Copyright (c) 2002-2005, Network Appliance, Inc. All rights reserved.
4  * Copyright (c) 1999-2005, Mellanox Technologies, Inc. All rights reserved.
5  * Copyright (c) 2005 Intel Corporation.  All rights reserved.
6  *
7  * This Software is licensed under one of the following licenses:
8  *
9  * 1) under the terms of the "Common Public License 1.0" a copy of which is
10  *    available from the Open Source Initiative, see
11  *    http://www.opensource.org/licenses/cpl.php.
12  *
13  * 2) under the terms of the "The BSD License" a copy of which is
14  *    available from the Open Source Initiative, see
15  *    http://www.opensource.org/licenses/bsd-license.php.
16  *
17  * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
18  *    copy of which is available from the Open Source Initiative, see
19  *    http://www.opensource.org/licenses/gpl-license.php.
20  *
21  * Licensee has the right to choose one of the above licenses.
22  *
23  * Redistributions of source code must retain the above copyright
24  * notice and one of the license notices.
25  *
26  * Redistributions in binary form must reproduce both the above copyright
27  * notice, one of the license notices in the documentation
28  * and/or other materials provided with the distribution.
29  *
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/condvar.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/socket.h>
40 #include <sys/module.h>
41
42 #include <sys/lock.h>
43 #include <sys/condvar.h>
44 #include <sys/mutex.h>
45 #include <sys/rwlock.h>
46 #include <sys/queue.h>
47 #include <sys/taskqueue.h>
48
49 #include <net/if.h>
50 #include <net/if_dl.h>
51 #include <net/if_var.h>
52 #include <net/if_arp.h>
53 #include <net/route.h>
54
55 #include <net80211/ieee80211_freebsd.h>
56
57 #include <netinet/in.h>
58 #include <netinet/if_ether.h>
59
60 #include <contrib/rdma/ib_addr.h>
61
62 struct addr_req {
63         TAILQ_ENTRY(addr_req) entry;
64         struct sockaddr src_addr;
65         struct sockaddr dst_addr;
66         struct rdma_dev_addr *addr;
67         struct rdma_addr_client *client;
68         void *context;
69         void (*callback)(int status, struct sockaddr *src_addr,
70                          struct rdma_dev_addr *addr, void *context);
71         unsigned long timeout;
72         int status;
73 };
74
75 static void process_req(void *ctx, int pending);
76
77 static struct mtx lock;
78
79 static TAILQ_HEAD(addr_req_list, addr_req) req_list;
80 static struct task addr_task;
81 static struct taskqueue *addr_taskq;
82 static struct callout addr_ch;
83 static eventhandler_tag route_event_tag;
84
85 static void addr_timeout(void *arg)
86 {
87         taskqueue_enqueue(addr_taskq, &addr_task);
88 }
89
90 void rdma_addr_register_client(struct rdma_addr_client *client)
91 {
92         mtx_init(&client->lock, "rdma_addr client lock", NULL, MTX_DUPOK|MTX_DEF);
93         cv_init(&client->comp, "rdma_addr cv");
94         client->refcount = 1;
95 }
96
97 static inline void put_client(struct rdma_addr_client *client)
98 {
99         mtx_lock(&client->lock);
100         if (--client->refcount == 0) {
101                 cv_broadcast(&client->comp);
102         }
103         mtx_unlock(&client->lock);
104 }
105
106 void rdma_addr_unregister_client(struct rdma_addr_client *client)
107 {
108         put_client(client);
109         mtx_lock(&client->lock);
110         if (client->refcount) {
111                 cv_wait(&client->comp, &client->lock);
112         }
113         mtx_unlock(&client->lock);
114 }
115
116 int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct ifnet *dev,
117                      const unsigned char *dst_dev_addr)
118 {
119         dev_addr->dev_type = RDMA_NODE_RNIC;
120         memset(dev_addr->src_dev_addr, 0, MAX_ADDR_LEN);
121         memcpy(dev_addr->src_dev_addr, IF_LLADDR(dev), dev->if_addrlen);
122         memcpy(dev_addr->broadcast, dev->if_broadcastaddr, MAX_ADDR_LEN);
123         if (dst_dev_addr)
124                 memcpy(dev_addr->dst_dev_addr, dst_dev_addr, MAX_ADDR_LEN);
125         return 0;
126 }
127
128 int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
129 {
130         struct ifaddr *ifa;
131         struct sockaddr_in *sin = (struct sockaddr_in *)addr;
132         uint16_t port = sin->sin_port;
133         int ret;
134         
135         sin->sin_port = 0;
136         ifa = ifa_ifwithaddr(addr);
137         sin->sin_port = port;
138         if (!ifa)
139                 return (EADDRNOTAVAIL);
140         ret = rdma_copy_addr(dev_addr, ifa->ifa_ifp, NULL);
141         ifa_free(ifa);
142         return (ret);
143 }
144
145 static void queue_req(struct addr_req *req)
146 {
147         struct addr_req *tmp_req = NULL;
148         
149         mtx_lock(&lock);
150         TAILQ_FOREACH_REVERSE(tmp_req, &req_list, addr_req_list, entry)
151             if (time_after_eq(req->timeout, tmp_req->timeout))
152                     break;
153         
154         if (tmp_req)
155                 TAILQ_INSERT_AFTER(&req_list, tmp_req, req, entry);
156         else
157                 TAILQ_INSERT_TAIL(&req_list, req, entry);
158         
159         if (TAILQ_FIRST(&req_list) == req)      
160                 callout_reset(&addr_ch, req->timeout - ticks, addr_timeout, NULL);
161         mtx_unlock(&lock);
162 }
163
164 #ifdef needed
165 static void addr_send_arp(struct sockaddr_in *dst_in)
166 {
167         struct route iproute;
168         struct sockaddr_in *dst = (struct sockaddr_in *)&iproute.ro_dst;
169         char dmac[ETHER_ADDR_LEN];
170         struct llentry *lle;
171
172         bzero(&iproute, sizeof iproute);
173         *dst = *dst_in;
174
175         rtalloc(&iproute);
176         if (iproute.ro_rt == NULL);
177                 return;
178
179         arpresolve(iproute.ro_rt->rt_ifp, iproute.ro_rt, NULL, 
180                    rt_key(iproute.ro_rt), dmac, &lle);
181
182         RTFREE(iproute.ro_rt);
183 }
184 #endif
185
186 static int addr_resolve_remote(struct sockaddr_in *src_in,
187                                struct sockaddr_in *dst_in,
188                                struct rdma_dev_addr *addr)
189 {
190         int ret = 0;
191         struct route iproute;
192         struct sockaddr_in *dst = (struct sockaddr_in *)&iproute.ro_dst;
193         char dmac[ETHER_ADDR_LEN];
194         struct llentry *lle;
195
196         bzero(&iproute, sizeof iproute);
197         *dst = *dst_in;
198
199         rtalloc(&iproute);
200         if (iproute.ro_rt == NULL) {
201                 ret = EHOSTUNREACH;
202                 goto out;
203         }
204
205         /* If the device does ARP internally, return 'done' */
206         if (iproute.ro_rt->rt_ifp->if_flags & IFF_NOARP) {
207                 rdma_copy_addr(addr, iproute.ro_rt->rt_ifp, NULL);
208                 goto put;
209         }
210         ret = arpresolve(iproute.ro_rt->rt_ifp, iproute.ro_rt, NULL, 
211                 (struct sockaddr *)dst_in, dmac, &lle);
212         if (ret) {
213                 goto put;
214         }
215
216         if (!src_in->sin_addr.s_addr) {
217                 src_in->sin_len = sizeof *src_in;
218                 src_in->sin_family = dst_in->sin_family;
219                 src_in->sin_addr.s_addr = ((struct sockaddr_in *)iproute.ro_rt->rt_ifa->ifa_addr)->sin_addr.s_addr;
220         }
221
222         ret = rdma_copy_addr(addr, iproute.ro_rt->rt_ifp, dmac);
223 put:
224         RTFREE(iproute.ro_rt);
225 out:
226         return ret;
227 }
228
229 static void process_req(void *ctx, int pending)
230 {
231         struct addr_req *req, *tmp_req;
232         struct sockaddr_in *src_in, *dst_in;
233         TAILQ_HEAD(, addr_req) done_list;
234
235         TAILQ_INIT(&done_list);
236
237         mtx_lock(&lock);
238         TAILQ_FOREACH_SAFE(req, &req_list, entry, tmp_req) {
239                 if (req->status == EWOULDBLOCK) {
240                         src_in = (struct sockaddr_in *) &req->src_addr;
241                         dst_in = (struct sockaddr_in *) &req->dst_addr;
242                         req->status = addr_resolve_remote(src_in, dst_in,
243                                                           req->addr);
244                         if (req->status && time_after_eq(ticks, req->timeout))
245                                 req->status = ETIMEDOUT;
246                         else if (req->status == EWOULDBLOCK)
247                                 continue;
248                 }
249                 TAILQ_REMOVE(&req_list, req, entry);
250                 TAILQ_INSERT_TAIL(&done_list, req, entry);
251         }
252
253         if (!TAILQ_EMPTY(&req_list)) {
254                 req = TAILQ_FIRST(&req_list);
255                 callout_reset(&addr_ch, req->timeout - ticks, addr_timeout, 
256                         NULL);
257         }
258         mtx_unlock(&lock);
259
260         TAILQ_FOREACH_SAFE(req, &done_list, entry, tmp_req) {
261                 TAILQ_REMOVE(&done_list, req, entry);
262                 req->callback(req->status, &req->src_addr, req->addr,
263                               req->context);
264                 put_client(req->client);
265                 free(req, M_DEVBUF);
266         }
267 }
268
269 int rdma_resolve_ip(struct rdma_addr_client *client,
270                     struct sockaddr *src_addr, struct sockaddr *dst_addr,
271                     struct rdma_dev_addr *addr, int timeout_ms,
272                     void (*callback)(int status, struct sockaddr *src_addr,
273                                      struct rdma_dev_addr *addr, void *context),
274                     void *context)
275 {
276         struct sockaddr_in *src_in, *dst_in;
277         struct addr_req *req;
278         int ret = 0;
279
280         req = malloc(sizeof *req, M_DEVBUF, M_NOWAIT);
281         if (!req)
282                 return (ENOMEM);
283         memset(req, 0, sizeof *req);
284
285         if (src_addr)
286                 memcpy(&req->src_addr, src_addr, ip_addr_size(src_addr));
287         memcpy(&req->dst_addr, dst_addr, ip_addr_size(dst_addr));
288         req->addr = addr;
289         req->callback = callback;
290         req->context = context;
291         req->client = client;
292         mtx_lock(&client->lock);
293         client->refcount++;
294         mtx_unlock(&client->lock);
295
296         src_in = (struct sockaddr_in *) &req->src_addr;
297         dst_in = (struct sockaddr_in *) &req->dst_addr;
298
299         req->status = addr_resolve_remote(src_in, dst_in, addr);
300
301         switch (req->status) {
302         case 0:
303                 req->timeout = ticks;
304                 queue_req(req);
305                 break;
306         case EWOULDBLOCK:
307                 req->timeout = msecs_to_ticks(timeout_ms) + ticks;
308                 queue_req(req);
309 #ifdef needed
310                 addr_send_arp(dst_in);
311 #endif
312                 break;
313         default:
314                 ret = req->status;
315                 mtx_lock(&client->lock);
316                 client->refcount--;
317                 mtx_unlock(&client->lock);
318                 free(req, M_DEVBUF);
319                 break;
320         }
321         return ret;
322 }
323
324 void rdma_addr_cancel(struct rdma_dev_addr *addr)
325 {
326         struct addr_req *req, *tmp_req;
327
328         mtx_lock(&lock);
329         TAILQ_FOREACH_SAFE(req, &req_list, entry, tmp_req) {
330                 if (req->addr == addr) {
331                         req->status = ECANCELED;
332                         req->timeout = ticks;
333                         TAILQ_REMOVE(&req_list, req, entry);
334                         TAILQ_INSERT_HEAD(&req_list, req, entry);
335                         callout_reset(&addr_ch, req->timeout - ticks, addr_timeout, NULL);
336                         break;
337                 }
338         }
339         mtx_unlock(&lock);
340 }
341
342 static void
343 route_event_arp_update(void *unused, struct rtentry *rt0, uint8_t *enaddr,
344         struct sockaddr *sa)
345 {
346                 callout_stop(&addr_ch);
347                 taskqueue_enqueue(addr_taskq, &addr_task);
348 }
349
350 static int addr_init(void)
351 {
352         TAILQ_INIT(&req_list);
353         mtx_init(&lock, "rdma_addr req_list lock", NULL, MTX_DEF);
354
355         addr_taskq = taskqueue_create("rdma_addr_taskq", M_NOWAIT,
356                 taskqueue_thread_enqueue, &addr_taskq);
357         if (addr_taskq == NULL) {
358                 printf("failed to allocate rdma_addr taskqueue\n");
359                 return (ENOMEM);
360         }
361         taskqueue_start_threads(&addr_taskq, 1, PI_NET, "rdma_addr taskq");
362         TASK_INIT(&addr_task, 0, process_req, NULL);
363
364         callout_init(&addr_ch, TRUE);
365
366         route_event_tag = EVENTHANDLER_REGISTER(route_arp_update_event, 
367                 route_event_arp_update, NULL, EVENTHANDLER_PRI_ANY);
368
369         return 0;
370 }
371
372 static void addr_cleanup(void)
373 {
374         EVENTHANDLER_DEREGISTER(route_event_arp_update, route_event_tag);
375         callout_stop(&addr_ch);
376         taskqueue_drain(addr_taskq, &addr_task);
377         taskqueue_free(addr_taskq);
378 }
379
380 static int 
381 addr_load(module_t mod, int cmd, void *arg)
382 {
383         int err = 0;
384
385         switch (cmd) {
386         case MOD_LOAD:
387                 printf("Loading rdma_addr.\n");
388
389                 addr_init();
390                 break;
391         case MOD_QUIESCE:
392                 break;
393         case MOD_UNLOAD:
394                 printf("Unloading rdma_addr.\n");
395                 addr_cleanup();
396                 break;
397         case MOD_SHUTDOWN:
398                 break;
399         default:
400                 err = EOPNOTSUPP;
401                 break;
402         }
403
404         return (err);
405 }
406
407 static moduledata_t mod_data = {
408         "rdma_addr",
409         addr_load,
410         0
411 };
412
413 MODULE_VERSION(rdma_addr, 1);
414 DECLARE_MODULE(rdma_addr, mod_data, SI_SUB_EXEC, SI_ORDER_ANY);