1 /*
2  * Copyright (c) 2014-2016 Intel Corporation, Inc.  All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  */
32 
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/time.h>
36 
37 #include <ofi_util.h>
38 #include <ofi.h>
39 
40 static DEFINE_LIST(fabric_list);
41 extern struct ofi_common_locks common_locks;
42 
ofi_fabric_insert(struct util_fabric * fabric)43 void ofi_fabric_insert(struct util_fabric *fabric)
44 {
45 	pthread_mutex_lock(&common_locks.util_fabric_lock);
46 	dlist_insert_tail(&fabric->list_entry, &fabric_list);
47 	pthread_mutex_unlock(&common_locks.util_fabric_lock);
48 }
49 
util_match_fabric(struct dlist_entry * item,const void * arg)50 static int util_match_fabric(struct dlist_entry *item, const void *arg)
51 {
52 	struct util_fabric *fabric;
53 	struct util_fabric_info *fabric_info = (struct util_fabric_info *)arg;
54 
55 	fabric = container_of(item, struct util_fabric, list_entry);
56 	return (fabric_info->prov == fabric->prov) &&
57 		!strcmp(fabric->name, fabric_info->name);
58 }
59 
ofi_fabric_remove(struct util_fabric * fabric)60 void ofi_fabric_remove(struct util_fabric *fabric)
61 {
62 	pthread_mutex_lock(&common_locks.util_fabric_lock);
63 	dlist_remove(&fabric->list_entry);
64 	pthread_mutex_unlock(&common_locks.util_fabric_lock);
65 }
66 
67 
ofi_fid_match(struct dlist_entry * entry,const void * fid)68 static int ofi_fid_match(struct dlist_entry *entry, const void *fid)
69 {
70 	struct fid_list_entry *item;
71 	item = container_of(entry, struct fid_list_entry, entry);
72 	return (item->fid == fid);
73 }
74 
fid_list_insert(struct dlist_entry * fid_list,fastlock_t * lock,struct fid * fid)75 int fid_list_insert(struct dlist_entry *fid_list, fastlock_t *lock,
76 		    struct fid *fid)
77 {
78 	int ret = 0;
79 	struct dlist_entry *entry;
80 	struct fid_list_entry *item;
81 
82 	fastlock_acquire(lock);
83 	entry = dlist_find_first_match(fid_list, ofi_fid_match, fid);
84 	if (entry)
85 		goto out;
86 
87 	item = calloc(1, sizeof(*item));
88 	if (!item) {
89 		ret = -FI_ENOMEM;
90 		goto out;
91 	}
92 
93 	item->fid = fid;
94 	dlist_insert_tail(&item->entry, fid_list);
95 out:
96 	fastlock_release(lock);
97 	return ret;
98 }
99 
fid_list_remove(struct dlist_entry * fid_list,fastlock_t * lock,struct fid * fid)100 void fid_list_remove(struct dlist_entry *fid_list, fastlock_t *lock,
101 		     struct fid *fid)
102 {
103 	struct fid_list_entry *item;
104 	struct dlist_entry *entry;
105 
106 	fastlock_acquire(lock);
107 	entry = dlist_remove_first_match(fid_list, ofi_fid_match, fid);
108 	fastlock_release(lock);
109 
110 	if (entry) {
111 		item = container_of(entry, struct fid_list_entry, entry);
112 		free(item);
113 	}
114 }
115 
util_find_domain(struct dlist_entry * item,const void * arg)116 static int util_find_domain(struct dlist_entry *item, const void *arg)
117 {
118 	const struct util_domain *domain;
119 	const struct fi_info *info = arg;
120 
121 	domain = container_of(item, struct util_domain, list_entry);
122 
123 	return !strcmp(domain->name, info->domain_attr->name) &&
124 		!((info->caps | info->domain_attr->caps) & ~domain->info_domain_caps) &&
125 		 (((info->mode | info->domain_attr->mode) &
126 		   domain->info_domain_mode) == domain->info_domain_mode) &&
127 		 ((info->domain_attr->mr_mode & domain->mr_mode) == domain->mr_mode);
128 }
129 
130 /*
131  * Produces 1 fi_info output for each fi_info entry in the provider's base
132  * list (stored with util_prov), subject to the base fi_info meeting the
133  * user's hints.
134  */
util_getinfo(const struct util_prov * util_prov,uint32_t version,const char * node,const char * service,uint64_t flags,const struct fi_info * hints,struct fi_info ** info)135 int util_getinfo(const struct util_prov *util_prov, uint32_t version,
136 		 const char *node, const char *service, uint64_t flags,
137 		 const struct fi_info *hints, struct fi_info **info)
138 {
139 	struct util_fabric *fabric;
140 	struct util_domain *domain;
141 	struct dlist_entry *item;
142 	const struct fi_provider *prov = util_prov->prov;
143 	struct util_fabric_info fabric_info;
144 	struct fi_info *saved_info;
145 	int ret, copy_dest;
146 
147 	FI_DBG(prov, FI_LOG_CORE, "checking info\n");
148 
149 	if ((flags & FI_SOURCE) && !node && !service) {
150 		FI_INFO(prov, FI_LOG_CORE,
151 			"FI_SOURCE set, but no node or service\n");
152 		return -FI_EINVAL;
153 	}
154 
155 	ret = ofi_prov_check_dup_info(util_prov, version, hints, info);
156 	if (ret)
157 		return ret;
158 
159 	ofi_alter_info(*info, hints, version);
160 
161 	saved_info = *info;
162 
163 	for (; *info; *info = (*info)->next) {
164 
165 		fabric_info.name = (*info)->fabric_attr->name;
166 		fabric_info.prov = util_prov->prov;
167 
168 		pthread_mutex_lock(&common_locks.util_fabric_lock);
169 		item = dlist_find_first_match(&fabric_list, util_match_fabric,
170 					      &fabric_info);
171 		if (item) {
172 			fabric = container_of(item, struct util_fabric, list_entry);
173 			FI_DBG(prov, FI_LOG_CORE, "Found opened fabric\n");
174 			(*info)->fabric_attr->fabric = &fabric->fabric_fid;
175 
176 			fastlock_acquire(&fabric->lock);
177 			item = dlist_find_first_match(&fabric->domain_list,
178 						      util_find_domain, *info);
179 			if (item) {
180 				FI_DBG(prov, FI_LOG_CORE,
181 				       "Found open domain\n");
182 				domain = container_of(item, struct util_domain,
183 						      list_entry);
184 				(*info)->domain_attr->domain =
185 						&domain->domain_fid;
186 			}
187 			fastlock_release(&fabric->lock);
188 
189 		}
190 		pthread_mutex_unlock(&common_locks.util_fabric_lock);
191 
192 		if (flags & FI_SOURCE) {
193 			ret = ofi_get_addr(&(*info)->addr_format, flags,
194 					  node, service, &(*info)->src_addr,
195 					  &(*info)->src_addrlen);
196 			if (ret) {
197 				FI_INFO(prov, FI_LOG_CORE,
198 					"source address not available\n");
199 				goto err;
200 			}
201 			copy_dest = (hints && hints->dest_addr);
202 		} else {
203 			if (node || service) {
204 				copy_dest = 0;
205 				ret = ofi_get_addr(&(*info)->addr_format,
206 						   flags, node, service,
207 						   &(*info)->dest_addr,
208 						   &(*info)->dest_addrlen);
209 				if (ret) {
210 					FI_INFO(prov, FI_LOG_CORE,
211 						"cannot resolve dest address\n");
212 					goto err;
213 				}
214 			} else {
215 				copy_dest = (hints && hints->dest_addr);
216 			}
217 
218 			if (hints && hints->src_addr) {
219 				(*info)->src_addr = mem_dup(hints->src_addr,
220 						    hints->src_addrlen);
221 				if (!(*info)->src_addr) {
222 					ret = -FI_ENOMEM;
223 					goto err;
224 				}
225 				(*info)->src_addrlen = hints->src_addrlen;
226 				(*info)->addr_format = hints->addr_format;
227 			}
228 		}
229 
230 		if (copy_dest) {
231 			(*info)->dest_addr = mem_dup(hints->dest_addr,
232 						     hints->dest_addrlen);
233 			if (!(*info)->dest_addr) {
234 				ret = -FI_ENOMEM;
235 				goto err;
236 			}
237 			(*info)->dest_addrlen = hints->dest_addrlen;
238 			(*info)->addr_format = hints->addr_format;
239 		}
240 
241 		if ((*info)->dest_addr && !(*info)->src_addr) {
242 			ret = ofi_get_src_addr((*info)->addr_format,
243 					       (*info)->dest_addr,
244 					       (*info)->dest_addrlen,
245 					       &(*info)->src_addr,
246 					       &(*info)->src_addrlen);
247 			if (ret) {
248 				FI_INFO(prov, FI_LOG_CORE,
249 					"cannot resolve source address\n");
250 			}
251 		}
252 	}
253 
254 	*info = saved_info;
255 
256 	return 0;
257 
258 err:
259 	fi_freeinfo(*info);
260 	return ret;
261 }
262 
util_set_netif_names(struct fi_info * info,struct ofi_addr_list_entry * addr_entry)263 static void util_set_netif_names(struct fi_info *info,
264 				 struct ofi_addr_list_entry *addr_entry)
265 {
266 	char *name;
267 
268 	name = strdup(addr_entry->net_name);
269 	if (name) {
270 		free(info->fabric_attr->name);
271 		info->fabric_attr->name = name;
272 	}
273 
274 	name = strdup(addr_entry->ifa_name);
275 	if (name) {
276 		free(info->domain_attr->name);
277 		info->domain_attr->name = name;
278 	}
279 }
280 
281 /*
282  * Produces 1 fi_info output for each usable IP address in the system for the
283  * given fi_info input.
284  */
285 #if HAVE_GETIFADDRS
util_getinfo_ifs(const struct util_prov * prov,struct fi_info * src_info,struct fi_info ** head,struct fi_info ** tail)286 static void util_getinfo_ifs(const struct util_prov *prov, struct fi_info *src_info,
287 			     struct fi_info **head, struct fi_info **tail)
288 {
289 	struct fi_info *cur;
290 	struct slist addr_list;
291 	size_t addrlen;
292 	uint32_t addr_format;
293 	struct slist_entry *entry, *prev;
294 	struct ofi_addr_list_entry *addr_entry;
295 
296 	*head = *tail = NULL;
297 	slist_init(&addr_list);
298 
299 	ofi_get_list_of_addr(prov->prov, "iface", &addr_list);
300 
301 	(void) prev; /* Makes compiler happy */
302 	slist_foreach(&addr_list, entry, prev) {
303 		addr_entry = container_of(entry, struct ofi_addr_list_entry, entry);
304 
305 		cur = fi_dupinfo(src_info);
306 		if (!cur)
307 			break;
308 
309 		if (!*head) {
310 			*head = cur;
311 			FI_INFO(prov->prov, FI_LOG_CORE, "Chosen addr for using: %s,"
312 				" speed %zu\n", addr_entry->ipstr, addr_entry->speed);
313 		} else {
314 			(*tail)->next = cur;
315 		}
316 		*tail = cur;
317 
318 		switch (addr_entry->ipaddr.sin.sin_family) {
319 		case AF_INET:
320 			addrlen = sizeof(struct sockaddr_in);
321 			addr_format = FI_SOCKADDR_IN;
322 			break;
323 		case AF_INET6:
324 			addrlen = sizeof(struct sockaddr_in6);
325 			addr_format = FI_SOCKADDR_IN6;
326 			break;
327 		default:
328 			continue;
329 		}
330 
331 		cur->src_addr = mem_dup(&addr_entry->ipaddr, addrlen);
332 		if (cur->src_addr) {
333 			cur->src_addrlen = addrlen;
334 			cur->addr_format = addr_format;
335 		}
336 		util_set_netif_names(cur, addr_entry);
337 	}
338 
339 	ofi_free_list_of_addr(&addr_list);
340 	if (!*head) {
341 		*head = src_info;
342 		*tail = src_info;
343 	}
344 }
345 #else
util_getinfo_ifs(const struct util_prov * prov,struct fi_info * src_info,struct fi_info ** head,struct fi_info ** tail)346 static void util_getinfo_ifs(const struct util_prov *prov, struct fi_info *src_info,
347 			     struct fi_info **head, struct fi_info **tail)
348 {
349 	*head = src_info;
350 	*tail = src_info;
351 }
352 #endif
353 
util_match_addr(struct slist_entry * entry,const void * addr)354 static int util_match_addr(struct slist_entry *entry, const void *addr)
355 {
356 	struct ofi_addr_list_entry *addr_entry;
357 
358 	addr_entry = container_of(entry, struct ofi_addr_list_entry, entry);
359 	return ofi_equals_ipaddr(&addr_entry->ipaddr.sa, addr);
360 }
361 
ofi_ip_getinfo(const struct util_prov * prov,uint32_t version,const char * node,const char * service,uint64_t flags,const struct fi_info * hints,struct fi_info ** info)362 int ofi_ip_getinfo(const struct util_prov *prov, uint32_t version,
363 		   const char *node, const char *service, uint64_t flags,
364 		   const struct fi_info *hints, struct fi_info **info)
365 {
366 	struct fi_info *head, *tail, *cur, **prev;
367 	struct ofi_addr_list_entry *addr_entry;
368 	struct slist addr_list;
369 	struct slist_entry *entry;
370 	int ret;
371 
372 	ret = util_getinfo(prov, version, node, service, flags,
373 			   hints, info);
374 	if (ret)
375 		return ret;
376 
377 	prev = info;
378 	for (cur = *info; cur; cur = cur->next) {
379 		if (!cur->src_addr && !cur->dest_addr) {
380 			util_getinfo_ifs(prov, cur, &head, &tail);
381 			if (head != cur) {
382 				tail->next = (*prev)->next;
383 				*prev = head;
384 
385 				cur->next = NULL;
386 				fi_freeinfo(cur);
387 				cur = tail;
388 			}
389 		} else if (cur->src_addr) {
390 			slist_init(&addr_list);
391 			ofi_get_list_of_addr(prov->prov, "iface", &addr_list);
392 
393 			entry = slist_find_first_match(&addr_list, util_match_addr,
394 						(*info)->src_addr);
395 			if (entry) {
396 				addr_entry = container_of(entry,
397 						struct ofi_addr_list_entry, entry);
398 				util_set_netif_names(cur, addr_entry);
399 			}
400 			ofi_free_list_of_addr(&addr_list);
401 		}
402 		prev = &cur->next;
403 	}
404 
405 	return 0;
406 }
407