1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24 #include <sys/types.h>
25 #include <sys/sunddi.h>
26 #include <sys/dlpi.h>
27 #include <sys/ib/clients/rdsv3/rdsv3_sc.h>
28 #include <sys/ib/clients/rdsv3/rdsv3_debug.h>
29
30 /*
31 * RDS Path MAP
32 *
33 * N - Node record, P - Path record
34 *
35 * rds_path_map -
36 * |
37 * v
38 * --------- --------- ---------
39 * | N |------>| N |------>| N |------> NULL
40 * NULL <-------| |<------| |<------| |
41 * --------- --------- ---------
42 * | | |
43 * | | |
44 * v v v
45 * -------- --------- ---------
46 * | P | | P | | P |
47 * -------- --------- ---------
48 * | ^ | ^ | ^
49 * | | | | | |
50 * v | v | v |
51 * -------- -------- ---------
52 * | P | | P | | P |
53 * -------- -------- ---------
54 * o o o
55 * o o o
56 * o o o
57 */
58
59 typedef struct rds_path_record_s {
60 ipaddr_t libd_ip;
61 ipaddr_t ribd_ip;
62 struct rds_path_record_s *up;
63 struct rds_path_record_s *downp;
64 char lifname[MAXNAMELEN];
65 char rifname[MAXNAMELEN];
66 } rds_path_record_t;
67
68 typedef struct rds_node_record_s {
69 struct rds_node_record_s *nextp;
70 ipaddr_t lnode_ip; /* local ip */
71 ipaddr_t rnode_ip; /* remote ip */
72 struct rds_path_record_s *downp;
73 struct rds_node_record_s *prevp;
74 } rds_node_record_t;
75
76 static char sc_device_name[MAXNAMELEN] = "NotInitialized";
77 static kmutex_t rdsv3_pathmap_lock;
78 static rds_node_record_t *rdsv3_pathmap = NULL;
79
80 #define RDS_VALIDATE_PATH(p) \
81 if ((p->local.iftype != DL_IB) || (p->remote.iftype != DL_IB)) \
82 return
83
84 #define isalpha(ch) (((ch) >= 'a' && (ch) <= 'z') || \
85 ((ch) >= 'A' && (ch) <= 'Z'))
86
87 /*
88 * Called by SC to register the Sun Cluster device name
89 */
90 void
rdsv3_clif_name(char * name)91 rdsv3_clif_name(char *name)
92 {
93 int i;
94
95 ASSERT(name != NULL);
96
97 mutex_enter(&rdsv3_pathmap_lock);
98
99 /* extract the device name from the interface name */
100 i = strlen(name) - 1;
101 while ((i >= 0) && (!isalpha(name[i]))) i--;
102 if (i >= 0) {
103 (void) strncpy(sc_device_name, name, i + 1);
104 sc_device_name[i + 1] = '\0';
105 }
106
107 mutex_exit(&rdsv3_pathmap_lock);
108 }
109
110 /*
111 * Called by SC on discovering a new path
112 */
113 void
rdsv3_path_up(rds_path_t * path)114 rdsv3_path_up(rds_path_t *path)
115 {
116 rds_node_record_t *p;
117 rds_path_record_t *p1;
118
119 ASSERT(path != NULL);
120
121 /* ignore if the end points are not of type DL_IB */
122 RDS_VALIDATE_PATH(path);
123
124 mutex_enter(&rdsv3_pathmap_lock);
125
126 p = rdsv3_pathmap;
127 while ((p) && ((p->lnode_ip != path->local.node_ipaddr) ||
128 (p->rnode_ip != path->remote.node_ipaddr))) {
129 p = p->nextp;
130 }
131
132 if (p == NULL) {
133 p = (rds_node_record_t *)kmem_alloc(sizeof (rds_node_record_t),
134 KM_SLEEP);
135 p1 = (rds_path_record_t *)kmem_alloc(
136 sizeof (rds_path_record_t), KM_SLEEP);
137
138 p->nextp = NULL;
139 p->lnode_ip = path->local.node_ipaddr;
140 p->rnode_ip = path->remote.node_ipaddr;
141 p->downp = p1;
142 p->prevp = NULL;
143
144 p1->libd_ip = path->local.ipaddr;
145 p1->ribd_ip = path->remote.ipaddr;
146 p1->up = NULL;
147 p1->downp = NULL;
148 (void) strcpy(p1->lifname, path->local.ifname);
149 (void) strcpy(p1->rifname, path->remote.ifname);
150
151 if (rdsv3_pathmap == NULL) {
152 rdsv3_pathmap = p;
153 } else {
154 /* insert this node at the head */
155 rdsv3_pathmap->prevp = p;
156 p->nextp = rdsv3_pathmap;
157 rdsv3_pathmap = p;
158 }
159 } else {
160 /* we found a match */
161 p1 = (rds_path_record_t *)kmem_alloc(
162 sizeof (rds_path_record_t), KM_SLEEP);
163
164 p1->libd_ip = path->local.ipaddr;
165 p1->ribd_ip = path->remote.ipaddr;
166 p1->downp = p->downp;
167 p->downp->up = p1;
168 p1->up = NULL;
169 p->downp = p1;
170 (void) strcpy(p1->lifname, path->local.ifname);
171 (void) strcpy(p1->rifname, path->remote.ifname);
172 }
173
174 mutex_exit(&rdsv3_pathmap_lock);
175 }
176
177 /*
178 * Called by SC to delete a path
179 */
180 void
rdsv3_path_down(rds_path_t * path)181 rdsv3_path_down(rds_path_t *path)
182 {
183 rds_node_record_t *p;
184 rds_path_record_t *p1, *p1up, *p1downp;
185
186 ASSERT(path != NULL);
187
188 /* ignore if the end points are not of type DL_IB */
189 RDS_VALIDATE_PATH(path);
190
191 mutex_enter(&rdsv3_pathmap_lock);
192
193 p = rdsv3_pathmap;
194 while ((p) && ((p->lnode_ip != path->local.node_ipaddr) ||
195 (p->rnode_ip != path->remote.node_ipaddr))) {
196 p = p->nextp;
197 }
198
199 if (p == NULL) {
200 /* no match */
201 RDSV3_DPRINTF2("rdsv3_path_down", "Node record not found "
202 "(0x%x <-> 0x%x)", path->local.node_ipaddr,
203 path->remote.node_ipaddr);
204 mutex_exit(&rdsv3_pathmap_lock);
205 return;
206 }
207
208 p1 = p->downp;
209 while ((p1) && ((p1->libd_ip != path->local.ipaddr) ||
210 (p1->ribd_ip != path->remote.ipaddr))) {
211 p1 = p1->downp;
212 }
213
214 if (p1 == NULL) {
215 /* no match */
216 RDSV3_DPRINTF2("rdsv3_path_down", "Path record not found "
217 "(0x%x <-> 0x%x)", path->local.ipaddr, path->remote.ipaddr);
218 mutex_exit(&rdsv3_pathmap_lock);
219 return;
220 }
221
222 /* we found the record, remove it */
223 p1up = p1->up;
224 p1downp = p1->downp;
225
226 if (p1up) {
227 p1up->downp = p1downp;
228 } else {
229 /* this is the first path record */
230 p->downp = p1downp;
231 }
232
233 if (p1downp) {
234 p1downp->up = p1up;
235 }
236
237 kmem_free(p1, sizeof (rds_path_record_t));
238
239 /* remove the node record if there are no path records */
240 if (p->downp == NULL) {
241 if (p->prevp) {
242 p->prevp->nextp = p->nextp;
243 } else {
244 /* this is the first node record */
245 ASSERT(p == rdsv3_pathmap);
246 rdsv3_pathmap = p->nextp;
247 }
248
249 if (p->nextp) {
250 p->nextp->prevp = p->prevp;
251 }
252
253 kmem_free(p, sizeof (rds_node_record_t));
254 }
255
256 mutex_exit(&rdsv3_pathmap_lock);
257 }
258
259 int
rdsv3_sc_path_lookup(ipaddr_t * localip,ipaddr_t * remip)260 rdsv3_sc_path_lookup(ipaddr_t *localip, ipaddr_t *remip)
261 {
262 rds_node_record_t *p;
263 rds_path_record_t *p1, *p1downp;
264
265 mutex_enter(&rdsv3_pathmap_lock);
266
267 p = rdsv3_pathmap;
268 while ((p) && ((p->lnode_ip != *localip) || (p->rnode_ip != *remip))) {
269 p = p->nextp;
270 }
271
272 if (p == NULL) {
273 /* no match */
274 RDSV3_DPRINTF2("rdsv3_sc_path_lookup", "Node record not found "
275 "(0x%x <-> 0x%x)", *localip, *remip);
276 mutex_exit(&rdsv3_pathmap_lock);
277 return (0);
278 }
279
280 /* found a path */
281 p1 = p->downp;
282 *localip = p1->libd_ip;
283 *remip = p1->ribd_ip;
284
285 /*
286 * But next time, we want to use a different path record so move this
287 * path record to the end.
288 */
289 p1downp = p1->downp;
290 if (p1downp != NULL) {
291 p->downp = p1downp;
292 p1downp->up = NULL;
293
294 /* walk down to the last path record */
295 while (p1downp->downp != NULL) {
296 p1downp = p1downp->downp;
297 }
298
299 /* Attach the first path record to the end */
300 p1downp->downp = p1;
301 p1->up = p1downp;
302 p1->downp = NULL;
303 }
304
305 mutex_exit(&rdsv3_pathmap_lock);
306
307 return (1);
308 }
309
310 boolean_t
rdsv3_if_lookup_by_name(char * devname)311 rdsv3_if_lookup_by_name(char *devname)
312 {
313 mutex_enter(&rdsv3_pathmap_lock);
314
315 /*
316 * Sun Cluster always names its interconnect virtual network interface
317 * as clprivnetx, so return TRUE if there is atleast one node record
318 * and the interface name is clprivnet something.
319 */
320 if (strcmp(devname, sc_device_name) == 0) {
321 /* clprivnet address */
322 mutex_exit(&rdsv3_pathmap_lock);
323 return (B_TRUE);
324 }
325
326 mutex_exit(&rdsv3_pathmap_lock);
327 return (B_FALSE);
328 }
329
330 boolean_t
rdsv3_if_lookup_by_addr(ipaddr_t addr)331 rdsv3_if_lookup_by_addr(ipaddr_t addr)
332 {
333 rds_node_record_t *p;
334 rds_path_record_t *p1;
335
336 mutex_enter(&rdsv3_pathmap_lock);
337
338 p = rdsv3_pathmap;
339 while ((p) && (p->lnode_ip != addr)) {
340 p1 = p->downp;
341 while ((p1) && (p1->libd_ip != addr)) {
342 p1 = p1->downp;
343 }
344
345 /* we found a match */
346 if (p1 != NULL)
347 break;
348
349 /* go to the next node record */
350 p = p->nextp;
351 }
352
353 mutex_exit(&rdsv3_pathmap_lock);
354 if (p == NULL) {
355 /* no match */
356 RDSV3_DPRINTF2("rds_if_lookup_by_addr",
357 "Addr: 0x%x not found", addr);
358 return (B_FALSE);
359 }
360
361 /* Found a matching node record */
362 return (B_TRUE);
363 }
364
365 /*
366 * If SC is configured then addr would be a clprivnet address. Find the
367 * node record and return the first IB address. If the node record is not
368 * found, then return addr as-is.
369 */
370 ipaddr_t
rdsv3_scaddr_to_ibaddr(ipaddr_t addr)371 rdsv3_scaddr_to_ibaddr(ipaddr_t addr)
372 {
373 rds_node_record_t *p;
374 rds_path_record_t *p1;
375 ipaddr_t ret = addr;
376
377 mutex_enter(&rdsv3_pathmap_lock);
378
379 p = rdsv3_pathmap;
380 while ((p) && (p->lnode_ip != addr)) {
381 /* go to the next node record */
382 p = p->nextp;
383 }
384
385 if (p != NULL) {
386 p1 = p->downp;
387 ret = p1->libd_ip;
388 RDSV3_DPRINTF3("rds_scaddr_to_ibaddr",
389 "Addr: 0x%x found: 0x%x", addr, p1->libd_ip);
390 }
391 mutex_exit(&rdsv3_pathmap_lock);
392
393 /* Found a matching node record */
394 return (ret);
395 }
396