xref: /freebsd/sys/cam/cam_queue.c (revision 601752d5a7bef087e755da5a2b158fa35cb51ccb)
1 /*
2  * CAM request queue management functions.
3  *
4  * Copyright (c) 1997 Justin T. Gibbs.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions, and the following disclaimer,
12  *    without modification, immediately at the beginning of the file.
13  * 2. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  *      $Id: cam_queue.c,v 1.1 1998/09/15 06:33:23 gibbs Exp $
29  */
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/types.h>
33 #include <sys/malloc.h>
34 
35 #include <cam/cam.h>
36 #include <cam/cam_ccb.h>
37 #include <cam/cam_queue.h>
38 #include <cam/cam_debug.h>
39 
40 static __inline int
41 		queue_cmp(cam_pinfo **queue_array, int i, int j);
42 static __inline void
43 		swap(cam_pinfo **queue_array, int i, int j);
44 static void	heap_up(cam_pinfo **queue_array, int new_index);
45 static void	heap_down(cam_pinfo **queue_array, int index,
46 			  int last_index);
47 
48 struct camq *
49 camq_alloc(int size)
50 {
51 	struct camq *camq;
52 
53 	camq = (struct camq *)malloc(sizeof(*camq), M_DEVBUF, M_NOWAIT);
54 	if (camq != NULL) {
55 		if (camq_init(camq, size) != 0) {
56 			free(camq, M_DEVBUF);
57 			camq = NULL;
58 		}
59 	}
60 	return (camq);
61 }
62 
63 int
64 camq_init(struct camq *camq, int size)
65 {
66 	bzero(camq, sizeof(*camq));
67 	camq->array_size = size;
68 	if (camq->array_size != 0) {
69 		camq->queue_array = (cam_pinfo**)malloc(size*sizeof(cam_pinfo*),
70 							M_DEVBUF, M_NOWAIT);
71 		if (camq->queue_array == NULL) {
72 			printf("camq_init: - cannot malloc array!\n");
73 			return (1);
74 		}
75 	}
76 	return (0);
77 }
78 
79 /*
80  * Free a camq structure.  This should only be called if a controller
81  * driver failes somehow during its attach routine or is unloaded and has
82  * obtained a camq structure.  The XPT should ensure that the queue
83  * is empty before calling this routine.
84  */
85 void
86 camq_free(struct camq *queue)
87 {
88 	if (queue != NULL) {
89 		camq_fini(queue);
90 		free(queue, M_DEVBUF);
91 	}
92 }
93 
94 void
95 camq_fini(struct camq *queue)
96 {
97 	if (queue->queue_array != NULL) {
98 		free(queue->queue_array, M_DEVBUF);
99 	}
100 }
101 
102 u_int32_t
103 camq_resize(struct camq *queue, int new_size)
104 {
105 	cam_pinfo **new_array;
106 
107 #ifdef DIAGNOSTIC
108 	if (new_size < queue->entries)
109 		panic("camq_resize: New queue size can't accomodate "
110 		      "queued entries.");
111 #endif
112 	new_array = (cam_pinfo **)malloc(new_size * sizeof(cam_pinfo *),
113 					 M_DEVBUF, M_NOWAIT);
114 	if (new_array == NULL) {
115 		/* Couldn't satisfy request */
116 		return (CAM_RESRC_UNAVAIL);
117 	}
118 	if (queue->queue_array != NULL) {
119 		bcopy(queue->queue_array, new_array,
120 		      queue->entries * sizeof(cam_pinfo *));
121 		free(queue->queue_array, M_DEVBUF);
122 	}
123 	queue->queue_array = new_array;
124 	queue->array_size = new_size;
125 	return (CAM_REQ_CMP);
126 }
127 
128 /*
129  * camq_insert: Given an array of cam_pinfo* elememnts with
130  * the Heap(0, num_elements) property and array_size - num_elements >= 1,
131  * output Heap(0, num_elements+1) including new_entry in the array.
132  */
133 void
134 camq_insert(struct camq *queue, cam_pinfo *new_entry)
135 {
136 #ifdef DIAGNOSTIC
137 	if (queue->entries >= queue->array_size)
138 		panic("camq_insert: Attempt to insert into a full queue");
139 #endif
140 	queue->queue_array[queue->entries] = new_entry;
141 	new_entry->index = queue->entries;
142 	if (queue->entries != 0)
143 		heap_up(queue->queue_array, queue->entries);
144 	queue->entries++;
145 }
146 
147 /*
148  * camq_remove:  Given an array of cam_pinfo* elevements with the
149  * Heap(0, num_elements) property and an index such that 0 <= index <=
150  * num_elements, remove that entry and restore the Heap(0, num_elements-1)
151  * property.
152  */
153 cam_pinfo *
154 camq_remove(struct camq *queue, int index)
155 {
156 	cam_pinfo *removed_entry;
157 
158 	if ((queue->entries - index) <= 0)
159 		return (NULL);
160 	removed_entry = queue->queue_array[index];
161 	queue->entries--;
162 	if (queue->entries != index) {
163 		queue->queue_array[index] = queue->queue_array[queue->entries];
164 		queue->queue_array[index]->index = index;
165 		heap_down(queue->queue_array, index, queue->entries);
166 	}
167 	removed_entry->index = CAM_UNQUEUED_INDEX;
168 	return (removed_entry);
169 }
170 
171 /*
172  * camq_change_priority:  Given an array of cam_pinfo* elements with the
173  * Heap(0, num_entries) property, an index such that 0 <= index <= num_elements,
174  * and an new priority for the element at index, change the priority of
175  * element index and restore the Heap(0, num_elements) property.
176  */
177 void
178 camq_change_priority(struct camq *queue, int index, u_int32_t new_priority)
179 {
180 	if (new_priority > queue->queue_array[index]->priority) {
181 		queue->queue_array[index]->priority = new_priority;
182 		heap_down(queue->queue_array, index, queue->entries);
183 	} else {
184 		/* new_priority <= old_priority */
185 		queue->queue_array[index]->priority = new_priority;
186 		heap_up(queue->queue_array, index);
187 	}
188 }
189 
190 struct cam_devq *
191 cam_devq_alloc(int devices, int openings)
192 {
193 	struct cam_devq *devq;
194 
195 	devq = (struct cam_devq *)malloc(sizeof(*devq), M_DEVBUF, M_NOWAIT);
196 	if (devq == NULL) {
197 		printf("cam_devq_alloc: - cannot malloc!\n");
198 		return (NULL);
199 	}
200 	if (cam_devq_init(devq, devices, openings) != 0) {
201 		free(devq, M_DEVBUF);
202 		return (NULL);
203 	}
204 
205 	return (devq);
206 }
207 
208 int
209 cam_devq_init(struct cam_devq *devq, int devices, int openings)
210 {
211 	bzero(devq, sizeof(*devq));
212 	if (camq_init(&devq->alloc_queue, devices) != 0) {
213 		return (1);
214 	}
215 	if (camq_init(&devq->send_queue, devices) != 0) {
216 		camq_fini(&devq->alloc_queue);
217 		return (1);
218 	}
219 	devq->alloc_openings = openings;
220 	devq->alloc_active = 0;
221 	devq->send_openings = openings;
222 	devq->send_active = 0;
223 	return (0);
224 }
225 
226 void
227 cam_devq_free(struct cam_devq *devq)
228 {
229 	camq_free(&devq->alloc_queue);
230 	camq_free(&devq->send_queue);
231 	free(devq, M_DEVBUF);
232 }
233 
234 u_int32_t
235 cam_devq_resize(struct cam_devq *camq, int devices)
236 {
237 	u_int32_t retval;
238 
239 	retval = camq_resize(&camq->alloc_queue, devices);
240 
241 	if (retval == CAM_REQ_CMP)
242 		retval = camq_resize(&camq->send_queue, devices);
243 
244 	return (retval);
245 }
246 
247 struct cam_ccbq *
248 cam_ccbq_alloc(int openings)
249 {
250 	struct cam_ccbq *ccbq;
251 
252 	ccbq = (struct cam_ccbq *)malloc(sizeof(*ccbq), M_DEVBUF, M_NOWAIT);
253 	if (ccbq == NULL) {
254 		printf("cam_ccbq_alloc: - cannot malloc!\n");
255 		return (NULL);
256 	}
257 	if (cam_ccbq_init(ccbq, openings) != 0) {
258 		free(ccbq, M_DEVBUF);
259 		return (NULL);
260 	}
261 
262 	return (ccbq);
263 }
264 
265 void
266 cam_ccbq_free(struct cam_ccbq *ccbq)
267 {
268 	if (ccbq) {
269 		camq_fini(&ccbq->queue);
270 		free(ccbq, M_DEVBUF);
271 	}
272 }
273 
274 u_int32_t
275 cam_ccbq_resize(struct cam_ccbq *ccbq, int new_size)
276 {
277 	int delta;
278 	int space_left;
279 
280 	delta = new_size - (ccbq->dev_active + ccbq->dev_openings);
281 	space_left = new_size
282 	    - ccbq->queue.entries
283 	    - ccbq->held
284 	    - ccbq->dev_active;
285 
286 	/*
287 	 * Only attempt to change the underlying queue size if we are
288 	 * shrinking it and there is space for all outstanding entries
289 	 * in the new array or we have been requested to grow the array.
290 	 * We don't fail in the case where we can't reduce the array size,
291 	 * but clients that care that the queue be "garbage collected"
292 	 * should detect this condition and call us again with the
293 	 * same size once the outstanding entries have been processed.
294 	 */
295 	if (space_left < 0
296 	 || camq_resize(&ccbq->queue, new_size) == CAM_REQ_CMP) {
297 		ccbq->devq_openings += delta;
298 		ccbq->dev_openings += delta;
299 		return (CAM_REQ_CMP);
300 	} else {
301 		return (CAM_RESRC_UNAVAIL);
302 	}
303 }
304 
305 int
306 cam_ccbq_init(struct cam_ccbq *ccbq, int openings)
307 {
308 	bzero(ccbq, sizeof(*ccbq));
309 	if (camq_init(&ccbq->queue, openings) != 0) {
310 		return (1);
311 	}
312 	ccbq->devq_openings = openings;
313 	ccbq->dev_openings = openings;
314 	TAILQ_INIT(&ccbq->active_ccbs);
315 	return (0);
316 }
317 
318 /*
319  * Heap routines for manipulating CAM queues.
320  */
321 /*
322  * queue_cmp: Given an array of cam_pinfo* elements and indexes i
323  * and j, return less than 0, 0, or greater than 0 if i is less than,
324  * equal too, or greater than j respectively.
325  */
326 static __inline int
327 queue_cmp(cam_pinfo **queue_array, int i, int j)
328 {
329 	if (queue_array[i]->priority == queue_array[j]->priority)
330 		return (  queue_array[i]->generation
331 			- queue_array[j]->generation );
332 	else
333 		return (  queue_array[i]->priority
334 			- queue_array[j]->priority );
335 }
336 
337 /*
338  * swap: Given an array of cam_pinfo* elements and indexes i and j,
339  * exchange elements i and j.
340  */
341 static __inline void
342 swap(cam_pinfo **queue_array, int i, int j)
343 {
344 	cam_pinfo *temp_qentry;
345 
346 	temp_qentry = queue_array[j];
347 	queue_array[j] = queue_array[i];
348 	queue_array[i] = temp_qentry;
349 	queue_array[j]->index = j;
350 	queue_array[i]->index = i;
351 }
352 
353 /*
354  * heap_up:  Given an array of cam_pinfo* elements with the
355  * Heap(0, new_index-1) property and a new element in location
356  * new_index, output Heap(0, new_index).
357  */
358 static void
359 heap_up(cam_pinfo **queue_array, int new_index)
360 {
361 	int child;
362 	int parent;
363 
364 	child = new_index;
365 
366 	while (child != 0) {
367 
368 		parent = (child - 1) >> 1;
369 		if (queue_cmp(queue_array, parent, child) <= 0)
370 			break;
371 		swap(queue_array, parent, child);
372 		child = parent;
373 	}
374 }
375 
376 /*
377  * heap_down:  Given an array of cam_pinfo* elements with the
378  * Heap(index + 1, num_entries - 1) property with index containing
379  * an unsorted entry, output Heap(0, num_entries - 1).
380  */
381 static void
382 heap_down(cam_pinfo **queue_array, int index, int num_entries)
383 {
384 	int child;
385 	int parent;
386 
387 	parent = index;
388 	child = (parent << 1) + 1;
389 	for (; child < num_entries; child = (parent << 1) + 1) {
390 
391 		if (child + 1 < num_entries) {
392 			/* child+1 is the right child of parent */
393 			if (queue_cmp(queue_array, child + 1, child) < 0)
394 				child++;
395 		}
396 		/* child is now the least child of parent */
397 		if (queue_cmp(queue_array, parent, child) <= 0)
398 			break;
399 		swap(queue_array, child, parent);
400 		parent = child;
401 	}
402 }
403 
404