1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3219820Sjeff * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4219820Sjeff * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5219820Sjeff *
6219820Sjeff * This software is available to you under a choice of one of two
7219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
8219820Sjeff * General Public License (GPL) Version 2, available from the file
9219820Sjeff * COPYING in the main directory of this source tree, or the
10219820Sjeff * OpenIB.org BSD license below:
11219820Sjeff *
12219820Sjeff *     Redistribution and use in source and binary forms, with or
13219820Sjeff *     without modification, are permitted provided that the following
14219820Sjeff *     conditions are met:
15219820Sjeff *
16219820Sjeff *      - Redistributions of source code must retain the above
17219820Sjeff *        copyright notice, this list of conditions and the following
18219820Sjeff *        disclaimer.
19219820Sjeff *
20219820Sjeff *      - Redistributions in binary form must reproduce the above
21219820Sjeff *        copyright notice, this list of conditions and the following
22219820Sjeff *        disclaimer in the documentation and/or other materials
23219820Sjeff *        provided with the distribution.
24219820Sjeff *
25219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32219820Sjeff * SOFTWARE.
33219820Sjeff *
34219820Sjeff */
35219820Sjeff
36219820Sjeff/*
37219820Sjeff * Abstract:
38219820Sjeff *    Implementation of osm_pkt_randomizer_t.
39219820Sjeff *
40219820Sjeff */
41219820Sjeff
42219820Sjeff#if HAVE_CONFIG_H
43219820Sjeff#  include <config.h>
44219820Sjeff#endif				/* HAVE_CONFIG_H */
45219820Sjeff
46219820Sjeff#include <vendor/osm_pkt_randomizer.h>
47219820Sjeff#include <stdlib.h>
48219820Sjeff#include <string.h>
49219820Sjeff
50219820Sjeff#ifndef WIN32
51219820Sjeff#include <sys/time.h>
52219820Sjeff#include <unistd.h>
53219820Sjeff#endif
54219820Sjeff
55219820Sjeff/**********************************************************************
56219820Sjeff * Return TRUE if the path is in a fault path, and FALSE otherwise.
57219820Sjeff * By in a fault path the meaning is that there is a path in the fault
58219820Sjeff * paths that the given path includes it.
59219820Sjeff * E.g: if there is a fault path: 0,1,4
60219820Sjeff * For the given path: 0,1,4,7 the return value will be TRUE, also for
61219820Sjeff * the given path: 0,1,4 the return value will be TRUE, but for
62219820Sjeff * the given paths: 0,1 or 0,3,1,4 - the return value will be FALSE.
63219820Sjeff **********************************************************************/
64219820Sjeffboolean_t
65219820Sjeff__osm_pkt_randomizer_is_path_in_fault_paths(IN osm_log_t * p_log,
66219820Sjeff					    IN osm_dr_path_t * p_dr_path,
67219820Sjeff					    IN osm_pkt_randomizer_t *
68219820Sjeff					    p_pkt_rand)
69219820Sjeff{
70219820Sjeff	boolean_t res = FALSE, found_path;
71219820Sjeff	osm_dr_path_t *p_found_dr_path;
72219820Sjeff	uint8_t ind1, ind2;
73219820Sjeff
74219820Sjeff	OSM_LOG_ENTER(p_log);
75219820Sjeff
76219820Sjeff	for (ind1 = 0; ind1 < p_pkt_rand->num_paths_initialized; ind1++) {
77219820Sjeff		found_path = TRUE;
78219820Sjeff		p_found_dr_path = &(p_pkt_rand->fault_dr_paths[ind1]);
79219820Sjeff		/* if the hop count of the found path is greater than the
80219820Sjeff		   hop count of the input path - then it is not part of it.
81219820Sjeff		   Check the next path. */
82219820Sjeff		if (p_found_dr_path->hop_count > p_dr_path->hop_count)
83219820Sjeff			continue;
84219820Sjeff
85219820Sjeff		/* go over all the ports in the found path and see if they match
86219820Sjeff		   the ports in the input path */
87219820Sjeff		for (ind2 = 0; ind2 <= p_found_dr_path->hop_count; ind2++)
88219820Sjeff			if (p_found_dr_path->path[ind2] !=
89219820Sjeff			    p_dr_path->path[ind2])
90219820Sjeff				found_path = FALSE;
91219820Sjeff
92219820Sjeff		/* If found_path is TRUE  then there is a full match of the path */
93219820Sjeff		if (found_path == TRUE) {
94219820Sjeff			OSM_LOG(p_log, OSM_LOG_VERBOSE,
95219820Sjeff				"Given path is in a fault path\n");
96219820Sjeff			res = TRUE;
97219820Sjeff			break;
98219820Sjeff		}
99219820Sjeff	}
100219820Sjeff
101219820Sjeff	OSM_LOG_EXIT(p_log);
102219820Sjeff	return res;
103219820Sjeff}
104219820Sjeff
105219820Sjeff/**********************************************************************
106219820Sjeff * For a given dr_path - return TRUE if the path should be dropped,
107219820Sjeff * return FALSE otherwise.
108219820Sjeff * The check uses random criteria in order to determine whether or not
109219820Sjeff * the path should be dropped.
110219820Sjeff * First - if not all paths are initialized, it randomally chooses if
111219820Sjeff * to use this path as a fault path or not.
112219820Sjeff * Second - if the path is in the fault paths (meaning - it is equal
113219820Sjeff * to or includes one of the fault paths) - then it randomally chooses
114219820Sjeff * if to drop it or not.
115219820Sjeff **********************************************************************/
116219820Sjeffboolean_t
117219820Sjeff__osm_pkt_randomizer_process_path(IN osm_log_t * p_log,
118219820Sjeff				  IN osm_pkt_randomizer_t * p_pkt_rand,
119219820Sjeff				  IN osm_dr_path_t * p_dr_path)
120219820Sjeff{
121219820Sjeff	boolean_t res = FALSE;
122219820Sjeff	static boolean_t rand_value_init = FALSE;
123219820Sjeff	static int rand_value;
124219820Sjeff	boolean_t in_fault_paths;
125219820Sjeff	uint8_t i;
126219820Sjeff	char buf[BUF_SIZE];
127219820Sjeff	char line[BUF_SIZE];
128219820Sjeff
129219820Sjeff	OSM_LOG_ENTER(p_log);
130219820Sjeff
131219820Sjeff	if (rand_value_init == FALSE) {
132219820Sjeff		int seed;
133219820Sjeff#ifdef WIN32
134219820Sjeff		SYSTEMTIME st;
135219820Sjeff#else
136219820Sjeff		struct timeval tv;
137219820Sjeff		struct timezone tz;
138219820Sjeff#endif				/*  WIN32 */
139219820Sjeff
140219820Sjeff		/* initiate the rand_value according to timeofday */
141219820Sjeff		rand_value_init = TRUE;
142219820Sjeff
143219820Sjeff#ifdef WIN32
144219820Sjeff		GetLocalTime(&st);
145219820Sjeff		seed = st.wMilliseconds;
146219820Sjeff#else
147219820Sjeff		gettimeofday(&tv, &tz);
148219820Sjeff		seed = tv.tv_usec;
149219820Sjeff#endif				/*  WIN32 */
150219820Sjeff
151219820Sjeff		srand(seed);
152219820Sjeff	}
153219820Sjeff
154219820Sjeff	/* If the hop_count is 1 - then this is a mad down to our local port - don't drop it */
155219820Sjeff	if (p_dr_path->hop_count <= 1)
156219820Sjeff		goto Exit;
157219820Sjeff
158219820Sjeff	rand_value = rand();
159219820Sjeff
160219820Sjeff	sprintf(buf, "Path: ");
161219820Sjeff	/* update the dr_path into the buf */
162219820Sjeff	for (i = 0; i <= p_dr_path->hop_count; i++) {
163219820Sjeff		sprintf(line, "[%X]", p_dr_path->path[i]);
164219820Sjeff		strcat(buf, line);
165219820Sjeff	}
166219820Sjeff
167219820Sjeff	/* Check if the path given is in one of the fault paths */
168219820Sjeff	in_fault_paths =
169219820Sjeff	    __osm_pkt_randomizer_is_path_in_fault_paths(p_log, p_dr_path,
170219820Sjeff							p_pkt_rand);
171219820Sjeff
172219820Sjeff	/* Check if all paths are initialized */
173219820Sjeff	if (p_pkt_rand->num_paths_initialized <
174219820Sjeff	    p_pkt_rand->osm_pkt_num_unstable_links) {
175219820Sjeff		/* Not all packets are initialized. */
176219820Sjeff		if (in_fault_paths == FALSE) {
177219820Sjeff			/* the path is not in the false paths. Check using the rand value
178219820Sjeff			   if to update it there or not. */
179219820Sjeff			if (rand_value %
180219820Sjeff			    (p_pkt_rand->osm_pkt_unstable_link_rate) == 0) {
181219820Sjeff				OSM_LOG(p_log, OSM_LOG_VERBOSE,
182219820Sjeff					"%s added to the fault_dr_paths list\n"
183219820Sjeff					"\t\t\t rand_value:%u, unstable_link_rate:%u \n",
184219820Sjeff					buf, rand_value,
185219820Sjeff					p_pkt_rand->osm_pkt_unstable_link_rate);
186219820Sjeff
187219820Sjeff				/* update the path in the fault paths */
188219820Sjeff				memcpy(&
189219820Sjeff				       (p_pkt_rand->
190219820Sjeff					fault_dr_paths[p_pkt_rand->
191219820Sjeff						       num_paths_initialized]),
192219820Sjeff				       p_dr_path, sizeof(osm_dr_path_t));
193219820Sjeff				p_pkt_rand->num_paths_initialized++;
194219820Sjeff				in_fault_paths = TRUE;
195219820Sjeff			}
196219820Sjeff		}
197219820Sjeff	}
198219820Sjeff
199219820Sjeff	if (in_fault_paths == FALSE) {
200219820Sjeff		/* If in_fault_paths is FALSE - just ignore the path */
201219820Sjeff		OSM_LOG(p_log, OSM_LOG_VERBOSE, "%s not in fault paths\n", buf);
202219820Sjeff		goto Exit;
203219820Sjeff	}
204219820Sjeff
205219820Sjeff	/* The path is in the fault paths. Need to choose (randomally if to drop it
206219820Sjeff	   or not. */
207219820Sjeff	rand_value = rand();
208219820Sjeff
209219820Sjeff	if (rand_value % (p_pkt_rand->osm_pkt_drop_rate) == 0) {
210219820Sjeff		/* drop the current packet */
211219820Sjeff		res = TRUE;
212219820Sjeff		OSM_LOG(p_log, OSM_LOG_VERBOSE, "Dropping path:%s\n", buf);
213219820Sjeff	}
214219820Sjeff
215219820SjeffExit:
216219820Sjeff	OSM_LOG_EXIT(p_log);
217219820Sjeff	return res;
218219820Sjeff}
219219820Sjeff
220219820Sjeff/**********************************************************************
221219820Sjeff **********************************************************************/
222219820Sjeffboolean_t
223219820Sjeffosm_pkt_randomizer_mad_drop(IN osm_log_t * p_log,
224219820Sjeff			    IN osm_pkt_randomizer_t * p_pkt_randomizer,
225219820Sjeff			    IN const ib_mad_t * p_mad)
226219820Sjeff{
227219820Sjeff	const ib_smp_t *p_smp;
228219820Sjeff	boolean_t res = FALSE;
229219820Sjeff	osm_dr_path_t dr_path;
230219820Sjeff
231219820Sjeff	OSM_LOG_ENTER(p_log);
232219820Sjeff
233219820Sjeff	p_smp = (ib_smp_t *) p_mad;
234219820Sjeff
235219820Sjeff	if (p_smp->mgmt_class != IB_MCLASS_SUBN_DIR)
236219820Sjeff		/* This is a lid route mad. Don't drop it */
237219820Sjeff		goto Exit;
238219820Sjeff
239219820Sjeff	osm_dr_path_init(&dr_path, 0,	/* The h_bind is not really important for us to save */
240219820Sjeff			 p_smp->hop_count, p_smp->initial_path);
241219820Sjeff
242219820Sjeff	if (__osm_pkt_randomizer_process_path
243219820Sjeff	    (p_log, p_pkt_randomizer, &dr_path)) {
244219820Sjeff		/* the mad should be dropped o */
245219820Sjeff		OSM_LOG(p_log, OSM_LOG_VERBOSE,
246219820Sjeff			"mad TID: 0x%" PRIx64 " is being dropped\n",
247219820Sjeff			cl_ntoh64(p_smp->trans_id));
248219820Sjeff		res = TRUE;
249219820Sjeff	}
250219820Sjeff
251219820SjeffExit:
252219820Sjeff	OSM_LOG_EXIT(p_log);
253219820Sjeff	return res;
254219820Sjeff}
255219820Sjeff
256219820Sjeff/**********************************************************************
257219820Sjeff **********************************************************************/
258219820Sjeffib_api_status_t
259219820Sjeffosm_pkt_randomizer_init(IN OUT osm_pkt_randomizer_t ** pp_pkt_randomizer,
260219820Sjeff			IN osm_log_t * p_log)
261219820Sjeff{
262219820Sjeff	uint8_t tmp;
263219820Sjeff	ib_api_status_t res = IB_SUCCESS;
264219820Sjeff
265219820Sjeff	OSM_LOG_ENTER(p_log);
266219820Sjeff
267219820Sjeff	*pp_pkt_randomizer = malloc(sizeof(osm_pkt_randomizer_t));
268219820Sjeff	if (*pp_pkt_randomizer == NULL) {
269219820Sjeff		res = IB_INSUFFICIENT_MEMORY;
270219820Sjeff		goto Exit;
271219820Sjeff	}
272219820Sjeff	memset(*pp_pkt_randomizer, 0, sizeof(osm_pkt_randomizer_t));
273219820Sjeff	(*pp_pkt_randomizer)->num_paths_initialized = 0;
274219820Sjeff
275219820Sjeff	tmp = atol(getenv("OSM_PKT_DROP_RATE"));
276219820Sjeff	(*pp_pkt_randomizer)->osm_pkt_drop_rate = tmp;
277219820Sjeff
278219820Sjeff	if (getenv("OSM_PKT_NUM_UNSTABLE_LINKS") != NULL
279219820Sjeff	    && (tmp = atol(getenv("OSM_PKT_NUM_UNSTABLE_LINKS"))) > 0)
280219820Sjeff		(*pp_pkt_randomizer)->osm_pkt_num_unstable_links = tmp;
281219820Sjeff	else
282219820Sjeff		(*pp_pkt_randomizer)->osm_pkt_num_unstable_links = 1;
283219820Sjeff
284219820Sjeff	if (getenv("OSM_PKT_UNSTABLE_LINK_RATE") != NULL
285219820Sjeff	    && (tmp = atol(getenv("OSM_PKT_UNSTABLE_LINK_RATE"))) > 0)
286219820Sjeff		(*pp_pkt_randomizer)->osm_pkt_unstable_link_rate = tmp;
287219820Sjeff	else
288219820Sjeff		(*pp_pkt_randomizer)->osm_pkt_unstable_link_rate = 20;
289219820Sjeff
290219820Sjeff	OSM_LOG(p_log, OSM_LOG_VERBOSE, "Using OSM_PKT_DROP_RATE=%u \n"
291219820Sjeff		"\t\t\t\t OSM_PKT_NUM_UNSTABLE_LINKS=%u \n"
292219820Sjeff		"\t\t\t\t OSM_PKT_UNSTABLE_LINK_RATE=%u \n",
293219820Sjeff		(*pp_pkt_randomizer)->osm_pkt_drop_rate,
294219820Sjeff		(*pp_pkt_randomizer)->osm_pkt_num_unstable_links,
295219820Sjeff		(*pp_pkt_randomizer)->osm_pkt_unstable_link_rate);
296219820Sjeff
297219820Sjeff	/* allocate the fault_dr_paths variable */
298219820Sjeff	/* It is the number of the paths that will be saved as fault = osm_pkt_num_unstable_links */
299219820Sjeff	(*pp_pkt_randomizer)->fault_dr_paths = malloc(sizeof(osm_dr_path_t) *
300219820Sjeff						      (*pp_pkt_randomizer)->
301219820Sjeff						      osm_pkt_num_unstable_links);
302219820Sjeff	if ((*pp_pkt_randomizer)->fault_dr_paths == NULL) {
303219820Sjeff		res = IB_INSUFFICIENT_MEMORY;
304219820Sjeff		goto Exit;
305219820Sjeff	}
306219820Sjeff
307219820Sjeff	memset((*pp_pkt_randomizer)->fault_dr_paths, 0,
308219820Sjeff	       sizeof(osm_dr_path_t) *
309219820Sjeff	       (*pp_pkt_randomizer)->osm_pkt_num_unstable_links);
310219820Sjeff
311219820SjeffExit:
312219820Sjeff	OSM_LOG_EXIT(p_log);
313219820Sjeff	return (res);
314219820Sjeff}
315219820Sjeff
316219820Sjeff/**********************************************************************
317219820Sjeff **********************************************************************/
318219820Sjeffvoid
319219820Sjeffosm_pkt_randomizer_destroy(IN OUT osm_pkt_randomizer_t ** pp_pkt_randomizer,
320219820Sjeff			   IN osm_log_t * p_log)
321219820Sjeff{
322219820Sjeff	OSM_LOG_ENTER(p_log);
323219820Sjeff
324219820Sjeff	if (*pp_pkt_randomizer != NULL) {
325219820Sjeff		free((*pp_pkt_randomizer)->fault_dr_paths);
326219820Sjeff		free(*pp_pkt_randomizer);
327219820Sjeff	}
328219820Sjeff	OSM_LOG_EXIT(p_log);
329219820Sjeff}
330