xref: /illumos-gate/usr/src/uts/common/io/1394/adapters/hci1394_tlabel.c (revision 2570281cf351044b6936651ce26dbe1f801dcbd8)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1999-2000 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 /*
28  * hci1394_tlabel.h
29  *   These routines track the tlabel usage for a 1394 adapter.
30  */
31 
32 #include <sys/kmem.h>
33 #include <sys/types.h>
34 #include <sys/conf.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 
38 #include <sys/1394/ieee1394.h>
39 #include <sys/1394/adapters/hci1394.h>
40 
41 
42 /*
43  * hci1394_tlabel_init()
44  *    Initialize the tlabel structures.  These structures will be protected
45  *    by a mutex at the iblock_cookie passed in.  Bad tlabels will be usable
46  *    when > reclaim_time_nS has gone by.  init() returns a handle to be used
47  *    for the rest of the tlabel functions.
48  */
49 void
hci1394_tlabel_init(hci1394_drvinfo_t * drvinfo,hrtime_t reclaim_time_nS,hci1394_tlabel_handle_t * tlabel_handle)50 hci1394_tlabel_init(hci1394_drvinfo_t *drvinfo, hrtime_t reclaim_time_nS,
51     hci1394_tlabel_handle_t *tlabel_handle)
52 {
53 	hci1394_tlabel_t *tstruct;
54 
55 
56 	ASSERT(tlabel_handle != NULL);
57 
58 	/* alloc space for tlabel data */
59 	tstruct = kmem_alloc(sizeof (hci1394_tlabel_t), KM_SLEEP);
60 
61 	/* setup handle which is returned from this function */
62 	*tlabel_handle = tstruct;
63 
64 	/*
65 	 * Initialize tlabel structure. We start with max node set to the
66 	 * maxiumum node we could have so that we make sure the arrays are
67 	 * initialized correctly in hci1394_tlabel_reset().
68 	 */
69 	tstruct->tb_drvinfo = drvinfo;
70 	tstruct->tb_reclaim_time = reclaim_time_nS;
71 	tstruct->tb_max_node = TLABEL_RANGE - 1;
72 	tstruct->tb_bcast_sent = B_FALSE;
73 
74 	mutex_init(&tstruct->tb_mutex, NULL, MUTEX_DRIVER,
75 	    drvinfo->di_iblock_cookie);
76 
77 	/*
78 	 * The mutex must be initialized before tlabel_reset()
79 	 * is called.  This is because tlabel_reset is also
80 	 * used in normal tlabel processing (i.e. not just during
81 	 * initialization)
82 	 */
83 	hci1394_tlabel_reset(tstruct);
84 }
85 
86 
87 /*
88  * hci1394_tlabel_fini()
89  *    Frees up the space allocated in init().  Notice that a pointer to the
90  *    handle is used for the parameter.  fini() will set your handle to NULL
91  *    before returning.
92  */
93 void
hci1394_tlabel_fini(hci1394_tlabel_handle_t * tlabel_handle)94 hci1394_tlabel_fini(hci1394_tlabel_handle_t *tlabel_handle)
95 {
96 	hci1394_tlabel_t *tstruct;
97 
98 
99 	ASSERT(tlabel_handle != NULL);
100 
101 	tstruct = (hci1394_tlabel_t *)*tlabel_handle;
102 
103 	mutex_destroy(&tstruct->tb_mutex);
104 	kmem_free(tstruct, sizeof (hci1394_tlabel_t));
105 
106 	/* set handle to null.  This helps catch bugs. */
107 	*tlabel_handle = NULL;
108 }
109 
110 
111 /*
112  * hci1394_tlabel_alloc()
113  *    alloc a tlabel based on the node id. If alloc fails, we are out of
114  *    tlabels for that node. See comments before set_reclaim_time() on when
115  *    bad tlabel's are free to be used again.
116  */
117 int
hci1394_tlabel_alloc(hci1394_tlabel_handle_t tlabel_handle,uint_t destination,hci1394_tlabel_info_t * tlabel_info)118 hci1394_tlabel_alloc(hci1394_tlabel_handle_t tlabel_handle, uint_t destination,
119     hci1394_tlabel_info_t *tlabel_info)
120 {
121 	uint_t node_number;
122 	uint_t index;
123 	uint64_t bad;
124 	uint64_t free;
125 	hrtime_t time;
126 	uint8_t last;
127 
128 
129 	ASSERT(tlabel_handle != NULL);
130 	ASSERT(tlabel_info != NULL);
131 
132 	/* copy destination into tlabel_info */
133 	tlabel_info->tbi_destination = destination;
134 
135 	/* figure out what node we are going to */
136 	node_number = IEEE1394_NODE_NUM(destination);
137 
138 	mutex_enter(&tlabel_handle->tb_mutex);
139 
140 	/*
141 	 * Keep track of if we have sent out a broadcast request and what the
142 	 * maximum # node we have sent to for reset processing optimization
143 	 */
144 	if (node_number == IEEE1394_BROADCAST_NODEID) {
145 		tlabel_handle->tb_bcast_sent = B_TRUE;
146 	} else if (node_number > tlabel_handle->tb_max_node) {
147 		tlabel_handle->tb_max_node = node_number;
148 	}
149 
150 	/* setup copies so we don't take up so much space :-) */
151 	bad = tlabel_handle->tb_bad[node_number];
152 	free = tlabel_handle->tb_free[node_number];
153 	time = tlabel_handle->tb_bad_timestamp[node_number];
154 	last = tlabel_handle->tb_last[node_number];
155 
156 	/*
157 	 * If there are any bad tlabels, see if the last bad tlabel recorded for
158 	 * this nodeid is now good to use. If so, add all bad tlabels for that
159 	 * node id back into the free list
160 	 *
161 	 * NOTE: This assumes that bad tlabels are infrequent.
162 	 */
163 	if (bad != 0) {
164 		if (gethrtime() > time) {
165 
166 			/* add the bad tlabels back into the free list */
167 			free |= bad;
168 
169 			/* clear the bad list */
170 			bad = 0;
171 		}
172 	}
173 
174 	/*
175 	 * Find a free tlabel.  This will break out of the loop once it finds a
176 	 * tlabel.  There are a total of TLABEL_RANGE tlabels.  The alloc
177 	 * rotates the check so that we don't always use the same tlabel. It
178 	 * stores the last tlabel used in last.
179 	 */
180 	for (index = 0; index < TLABEL_RANGE; index++) {
181 
182 		/* if the next tlabel to check is free */
183 		if ((free & ((uint64_t)1 << last)) != 0) {
184 			/* we are using this tlabel */
185 			tlabel_info->tbi_tlabel = last;
186 
187 			/* take it out of the free list */
188 			free = free & ~((uint64_t)1 << last);
189 
190 			/*
191 			 * increment the last count so we start checking on the
192 			 * next tlabel next alloc().  Note the rollover at
193 			 * TLABEL_RANGE since we only have TLABEL_RANGE tlabels.
194 			 */
195 			(last)++;
196 			if (last >= TLABEL_RANGE) {
197 				last = 0;
198 			}
199 
200 			/* Copy the copies back */
201 			tlabel_handle->tb_bad[node_number] = bad;
202 			tlabel_handle->tb_free[node_number] = free;
203 			tlabel_handle->tb_bad_timestamp[node_number] = time;
204 			tlabel_handle->tb_last[node_number] = last;
205 
206 			/* unlock the tlabel structure */
207 			mutex_exit(&tlabel_handle->tb_mutex);
208 			return (DDI_SUCCESS);
209 		}
210 
211 		/*
212 		 * This tlabel is not free, lets go to the next one. Note the
213 		 * rollover at TLABEL_RANGE since we only have TLABEL_RANGE
214 		 * tlabels.
215 		 */
216 		(last)++;
217 		if (last >= TLABEL_RANGE) {
218 			last = 0;
219 		}
220 	}
221 
222 	/* Copy the copies back */
223 	tlabel_handle->tb_bad[node_number] = bad;
224 	tlabel_handle->tb_free[node_number] = free;
225 	tlabel_handle->tb_bad_timestamp[node_number] = time;
226 	tlabel_handle->tb_last[node_number] = last;
227 
228 	mutex_exit(&tlabel_handle->tb_mutex);
229 
230 	return (DDI_FAILURE);
231 }
232 
233 
234 /*
235  * hci1394_tlabel_free()
236  *    free the previously alloc()'d tlabel.  Once a tlabel has been free'd, it
237  *    can be used again when alloc() is called.
238  */
239 void
hci1394_tlabel_free(hci1394_tlabel_handle_t tlabel_handle,hci1394_tlabel_info_t * tlabel_info)240 hci1394_tlabel_free(hci1394_tlabel_handle_t tlabel_handle,
241     hci1394_tlabel_info_t *tlabel_info)
242 {
243 	uint_t node_number;
244 	uint_t tlabel;
245 
246 
247 	ASSERT(tlabel_handle != NULL);
248 	ASSERT(tlabel_info != NULL);
249 	ASSERT(tlabel_info->tbi_tlabel <= TLABEL_MASK);
250 
251 	/* figure out what node and tlabel we are using */
252 	node_number = IEEE1394_NODE_NUM(tlabel_info->tbi_destination);
253 	tlabel = tlabel_info->tbi_tlabel;
254 
255 	mutex_enter(&tlabel_handle->tb_mutex);
256 
257 	/*
258 	 * Put the tlabel back in the free list and NULL out the (void *) in the
259 	 * lookup structure.  You wouldn't expect to have to null out the lookup
260 	 * structure, but we know first hand that bad HW will send invalid
261 	 * tlabels which could really mess things up if you didn't :-)
262 	 */
263 	tlabel_handle->tb_lookup[node_number][tlabel] = NULL;
264 	tlabel_handle->tb_free[node_number] |= ((uint64_t)1 << tlabel);
265 
266 	mutex_exit(&tlabel_handle->tb_mutex);
267 }
268 
269 
270 /*
271  * hci1394_tlabel_register()
272  *    Register an opaque command with an alloc()'d tlabel. Each nodeID has it's
273  *    own tlabel list.
274  */
275 void
hci1394_tlabel_register(hci1394_tlabel_handle_t tlabel_handle,hci1394_tlabel_info_t * tlabel_info,void * cmd)276 hci1394_tlabel_register(hci1394_tlabel_handle_t tlabel_handle,
277     hci1394_tlabel_info_t *tlabel_info, void *cmd)
278 {
279 	uint_t node_number;
280 	uint_t tlabel;
281 
282 
283 	ASSERT(tlabel_handle != NULL);
284 	ASSERT(tlabel_info != NULL);
285 	ASSERT(tlabel_info->tbi_tlabel <= TLABEL_MASK);
286 
287 	/* figure out what node and tlabel we are using */
288 	node_number = IEEE1394_NODE_NUM(tlabel_info->tbi_destination);
289 	tlabel = tlabel_info->tbi_tlabel;
290 
291 	mutex_enter(&tlabel_handle->tb_mutex);
292 
293 	/* enter the (void *) into the lookup table */
294 	tlabel_handle->tb_lookup[node_number][tlabel] = cmd;
295 
296 	mutex_exit(&tlabel_handle->tb_mutex);
297 }
298 
299 
300 /*
301  * hci1394_tlabel_lookup()
302  *    returns (in cmd) the opaque command which was registered with the
303  *    specified tlabel from alloc(). If a tlabel was not registered, cmd ='s
304  *    NULL.
305  */
306 void
hci1394_tlabel_lookup(hci1394_tlabel_handle_t tlabel_handle,hci1394_tlabel_info_t * tlabel_info,void ** cmd)307 hci1394_tlabel_lookup(hci1394_tlabel_handle_t tlabel_handle,
308     hci1394_tlabel_info_t *tlabel_info, void **cmd)
309 {
310 	uint_t node_number;
311 	uint_t tlabel;
312 
313 
314 	ASSERT(tlabel_handle != NULL);
315 	ASSERT(tlabel_info != NULL);
316 	ASSERT(cmd != NULL);
317 	ASSERT(tlabel_info->tbi_tlabel <= TLABEL_MASK);
318 
319 	/* figure out what node and tlabel we are using */
320 	node_number = IEEE1394_NODE_NUM(tlabel_info->tbi_destination);
321 	tlabel = tlabel_info->tbi_tlabel;
322 
323 	mutex_enter(&tlabel_handle->tb_mutex);
324 
325 	/*
326 	 * fetch the (void *) from the lookup table.  The case where the pointer
327 	 * equals NULL will be handled by the layer above.
328 	 */
329 	*cmd = tlabel_handle->tb_lookup[node_number][tlabel];
330 
331 	mutex_exit(&tlabel_handle->tb_mutex);
332 }
333 
334 
335 /*
336  * hci1394_tlabel_bad()
337  *    Register the specified tlabel as bad.  tlabel_lookup() will no longer
338  *    return a registered opaque command and this tlabel will not be returned
339  *    from alloc() until > reclaim_time has passed. See set_reclaim_time() for
340  *    more info.
341  */
342 void
hci1394_tlabel_bad(hci1394_tlabel_handle_t tlabel_handle,hci1394_tlabel_info_t * tlabel_info)343 hci1394_tlabel_bad(hci1394_tlabel_handle_t tlabel_handle,
344     hci1394_tlabel_info_t *tlabel_info)
345 {
346 	uint_t node_number;
347 	uint_t tlabel;
348 
349 
350 	ASSERT(tlabel_handle != NULL);
351 	ASSERT(tlabel_info != NULL);
352 
353 	/* figure out what node and tlabel we are using */
354 	node_number = IEEE1394_NODE_NUM(tlabel_info->tbi_destination);
355 	tlabel = tlabel_info->tbi_tlabel & TLABEL_MASK;
356 
357 	mutex_enter(&tlabel_handle->tb_mutex);
358 
359 	/*
360 	 * Put the tlabel in the bad list and NULL out the (void *) in the
361 	 * lookup structure.  We may see this tlabel shortly if the device is
362 	 * late in responding. We want to make sure to drop the message if we
363 	 * do. Set the bad timestamp to the current time plus the reclaim time.
364 	 * This is the "new" time when all of the bad tlabels for this node will
365 	 * be free'd.
366 	 */
367 	tlabel_handle->tb_bad_timestamp[node_number] = gethrtime() +
368 	    tlabel_handle->tb_reclaim_time;
369 	tlabel_handle->tb_bad[node_number] |= ((uint64_t)1 << tlabel);
370 	tlabel_handle->tb_lookup[node_number][tlabel] = NULL;
371 
372 	mutex_exit(&tlabel_handle->tb_mutex);
373 }
374 
375 
376 /*
377  * hci1394_tlabel_reset()
378  *    resets the tlabel tracking structures to an initial state where no
379  *    tlabels are outstanding and all tlabels are registered as good.  This
380  *    routine should be called every bus reset.
381  */
382 void
hci1394_tlabel_reset(hci1394_tlabel_handle_t tlabel_handle)383 hci1394_tlabel_reset(hci1394_tlabel_handle_t tlabel_handle)
384 {
385 	int index;
386 	int index2;
387 
388 
389 	ASSERT(tlabel_handle != NULL);
390 
391 	mutex_enter(&tlabel_handle->tb_mutex);
392 
393 	/* Bus reset optimization. handle broadcast writes separately */
394 	if (tlabel_handle->tb_bcast_sent == B_TRUE) {
395 		tlabel_handle->tb_free[IEEE1394_BROADCAST_NODEID] =
396 		    (uint64_t)0xFFFFFFFFFFFFFFFF;
397 		tlabel_handle->tb_bad[IEEE1394_BROADCAST_NODEID] =
398 		    (uint64_t)0;
399 		tlabel_handle->tb_bad_timestamp[IEEE1394_BROADCAST_NODEID] =
400 		    (hrtime_t)0;
401 		tlabel_handle->tb_last[IEEE1394_BROADCAST_NODEID] = 0;
402 		for (index2 = 0; index2 < TLABEL_RANGE; index2++) {
403 			tlabel_handle->tb_lookup[IEEE1394_BROADCAST_NODEID
404 			    ][index2] = NULL;
405 		}
406 	}
407 
408 	/*
409 	 * Mark all tlabels as free.  No bad tlabels.  Start the first tlabel
410 	 * alloc at 0.  Cleanout the lookup table.  An optimization to only do
411 	 * this up to the max node we have seen on the bus has been added.
412 	 */
413 	for (index = 0; index <= tlabel_handle->tb_max_node; index++) {
414 		tlabel_handle->tb_free[index] = (uint64_t)0xFFFFFFFFFFFFFFFF;
415 		tlabel_handle->tb_bad[index] = (uint64_t)0;
416 		tlabel_handle->tb_bad_timestamp[index] = (hrtime_t)0;
417 		tlabel_handle->tb_last[index] = 0;
418 		for (index2 = 0; index2 < TLABEL_RANGE; index2++) {
419 			tlabel_handle->tb_lookup[index][index2] = NULL;
420 		}
421 	}
422 
423 	tlabel_handle->tb_max_node = 0;
424 	tlabel_handle->tb_bcast_sent = B_FALSE;
425 
426 	mutex_exit(&tlabel_handle->tb_mutex);
427 }
428 
429 
430 /*
431  * hci1394_tlabel_set_reclaim_time()
432  *    This function should be called if a change to the reclaim_time is
433  *    required after the initial call to init().  It is not necessary to call
434  *    this function if the reclaim time never changes.
435  *
436  *    Currently, bad tlabels are reclaimed in tlabel_alloc().
437  *    It looks like the following for a given node:
438  *
439  *    if bad tlabels exist
440  *	    if ((current time + reclaim time) >= last bad tlabel time)
441  *		    free all bad tlabels.
442  */
443 void
hci1394_tlabel_set_reclaim_time(hci1394_tlabel_handle_t tlabel_handle,hrtime_t reclaim_time_nS)444 hci1394_tlabel_set_reclaim_time(hci1394_tlabel_handle_t tlabel_handle,
445     hrtime_t reclaim_time_nS)
446 {
447 	ASSERT(tlabel_handle != NULL);
448 
449 	/*
450 	 * We do not need to lock the tlabel structure in this because we are
451 	 * doing a single write to reclaim_time. If this changes in the future,
452 	 * we may need to add calls to lock() and unlock().
453 	 */
454 	tlabel_handle->tb_reclaim_time = reclaim_time_nS;
455 }
456