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