diff --git a/sys/cam/cam_queue.c b/sys/cam/cam_queue.c index 5ad765583020..98d03283b80b 100644 --- a/sys/cam/cam_queue.c +++ b/sys/cam/cam_queue.c @@ -1,377 +1,373 @@ /*- * CAM request queue management functions. * * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1997 Justin T. Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_CAMQ, "CAM queue", "CAM queue buffers"); static MALLOC_DEFINE(M_CAMDEVQ, "CAM dev queue", "CAM dev queue buffers"); static MALLOC_DEFINE(M_CAMCCBQ, "CAM ccb queue", "CAM ccb queue buffers"); -static __inline int - queue_cmp(cam_pinfo **queue_array, int i, int j); -static __inline void - swap(cam_pinfo **queue_array, int i, int j); static void heap_up(cam_pinfo **queue_array, int new_index); static void heap_down(cam_pinfo **queue_array, int index, int last_index); int camq_init(struct camq *camq, int size) { bzero(camq, sizeof(*camq)); camq->array_size = size; if (camq->array_size != 0) { /* * Heap algorithms like everything numbered from 1, so * allocate one more to account for 0 base. */ camq->queue_array = malloc((size + 1) * sizeof(cam_pinfo*), M_CAMQ, M_NOWAIT); if (camq->queue_array == NULL) { printf("camq_init: - cannot malloc array!\n"); return (1); } } return (0); } /* * Free a camq structure. This should only be called if a controller * driver fails somehow during its attach routine or is unloaded and has * obtained a camq structure. The XPT should ensure that the queue * is empty before calling this routine. */ void camq_fini(struct camq *queue) { if (queue->queue_array != NULL) { free(queue->queue_array, M_CAMQ); } } u_int32_t camq_resize(struct camq *queue, int new_size) { cam_pinfo **new_array; KASSERT(new_size >= queue->entries, ("camq_resize: " "New queue size can't accommodate queued entries (%d < %d).", new_size, queue->entries)); new_array = malloc((new_size + 1) * sizeof(cam_pinfo *), M_CAMQ, M_NOWAIT); if (new_array == NULL) { /* Couldn't satisfy request */ return (CAM_RESRC_UNAVAIL); } /* * Heap algorithms like everything numbered from 1, so * remember that our pointer into the heap array is offset * by one element. */ if (queue->queue_array != NULL) { bcopy(queue->queue_array, new_array, (queue->entries + 1) * sizeof(cam_pinfo *)); free(queue->queue_array, M_CAMQ); } queue->queue_array = new_array; queue->array_size = new_size; return (CAM_REQ_CMP); } /* * camq_insert: Given an array of cam_pinfo* elememnts with * the Heap(1, num_elements) property and array_size - num_elements >= 1, * output Heap(1, num_elements+1) including new_entry in the array. */ void camq_insert(struct camq *queue, cam_pinfo *new_entry) { KASSERT(queue->entries < queue->array_size, ("camq_insert: Attempt to insert into a full queue (%d >= %d)", queue->entries, queue->array_size)); queue->entries++; queue->queue_array[queue->entries] = new_entry; new_entry->index = queue->entries; if (queue->entries != 0) heap_up(queue->queue_array, queue->entries); } /* * camq_remove: Given an array of cam_pinfo* elevements with the * Heap(1, num_elements) property and an index such that 1 <= index <= * num_elements, remove that entry and restore the Heap(1, num_elements-1) * property. */ cam_pinfo * camq_remove(struct camq *queue, int index) { cam_pinfo *removed_entry; if (index <= 0 || index > queue->entries) panic("%s: Attempt to remove out-of-bounds index %d " "from queue %p of size %d", __func__, index, queue, queue->entries); removed_entry = queue->queue_array[index]; if (queue->entries != index) { queue->queue_array[index] = queue->queue_array[queue->entries]; queue->queue_array[index]->index = index; heap_down(queue->queue_array, index, queue->entries - 1); } removed_entry->index = CAM_UNQUEUED_INDEX; queue->entries--; return (removed_entry); } /* * camq_change_priority: Given an array of cam_pinfo* elements with the * Heap(1, num_entries) property, an index such that 1 <= index <= num_elements, * and a new priority for the element at index, change the priority of * element index and restore the Heap(0, num_elements) property. */ void camq_change_priority(struct camq *queue, int index, u_int32_t new_priority) { if (new_priority > queue->queue_array[index]->priority) { queue->queue_array[index]->priority = new_priority; heap_down(queue->queue_array, index, queue->entries); } else { /* new_priority <= old_priority */ queue->queue_array[index]->priority = new_priority; heap_up(queue->queue_array, index); } } struct cam_devq * cam_devq_alloc(int devices, int openings) { struct cam_devq *devq; devq = (struct cam_devq *)malloc(sizeof(*devq), M_CAMDEVQ, M_NOWAIT); if (devq == NULL) { printf("cam_devq_alloc: - cannot malloc!\n"); return (NULL); } if (cam_devq_init(devq, devices, openings) != 0) { free(devq, M_CAMDEVQ); return (NULL); } return (devq); } int cam_devq_init(struct cam_devq *devq, int devices, int openings) { bzero(devq, sizeof(*devq)); mtx_init(&devq->send_mtx, "CAM queue lock", NULL, MTX_DEF); if (camq_init(&devq->send_queue, devices) != 0) return (1); devq->send_openings = openings; devq->send_active = 0; return (0); } void cam_devq_free(struct cam_devq *devq) { camq_fini(&devq->send_queue); mtx_destroy(&devq->send_mtx); free(devq, M_CAMDEVQ); } u_int32_t cam_devq_resize(struct cam_devq *camq, int devices) { u_int32_t retval; retval = camq_resize(&camq->send_queue, devices); return (retval); } struct cam_ccbq * cam_ccbq_alloc(int openings) { struct cam_ccbq *ccbq; ccbq = (struct cam_ccbq *)malloc(sizeof(*ccbq), M_CAMCCBQ, M_NOWAIT); if (ccbq == NULL) { printf("cam_ccbq_alloc: - cannot malloc!\n"); return (NULL); } if (cam_ccbq_init(ccbq, openings) != 0) { free(ccbq, M_CAMCCBQ); return (NULL); } return (ccbq); } void cam_ccbq_free(struct cam_ccbq *ccbq) { if (ccbq) { cam_ccbq_fini(ccbq); free(ccbq, M_CAMCCBQ); } } u_int32_t cam_ccbq_resize(struct cam_ccbq *ccbq, int new_size) { int delta; delta = new_size - (ccbq->dev_active + ccbq->dev_openings); ccbq->total_openings += delta; ccbq->dev_openings += delta; new_size = imax(64, 1 << fls(new_size + new_size / 2)); if (new_size > ccbq->queue.array_size) return (camq_resize(&ccbq->queue, new_size)); else return (CAM_REQ_CMP); } int cam_ccbq_init(struct cam_ccbq *ccbq, int openings) { bzero(ccbq, sizeof(*ccbq)); if (camq_init(&ccbq->queue, imax(64, 1 << fls(openings + openings / 2))) != 0) return (1); ccbq->total_openings = openings; ccbq->dev_openings = openings; return (0); } void cam_ccbq_fini(struct cam_ccbq *ccbq) { camq_fini(&ccbq->queue); } /* * Heap routines for manipulating CAM queues. */ /* * queue_cmp: Given an array of cam_pinfo* elements and indexes i * and j, return less than 0, 0, or greater than 0 if i is less than, * equal too, or greater than j respectively. */ static __inline int queue_cmp(cam_pinfo **queue_array, int i, int j) { if (queue_array[i]->priority == queue_array[j]->priority) return ( queue_array[i]->generation - queue_array[j]->generation ); else return ( queue_array[i]->priority - queue_array[j]->priority ); } /* * swap: Given an array of cam_pinfo* elements and indexes i and j, * exchange elements i and j. */ static __inline void swap(cam_pinfo **queue_array, int i, int j) { cam_pinfo *temp_qentry; temp_qentry = queue_array[j]; queue_array[j] = queue_array[i]; queue_array[i] = temp_qentry; queue_array[j]->index = j; queue_array[i]->index = i; } /* * heap_up: Given an array of cam_pinfo* elements with the * Heap(1, new_index-1) property and a new element in location * new_index, output Heap(1, new_index). */ static void heap_up(cam_pinfo **queue_array, int new_index) { int child; int parent; child = new_index; while (child != 1) { parent = child >> 1; if (queue_cmp(queue_array, parent, child) <= 0) break; swap(queue_array, parent, child); child = parent; } } /* * heap_down: Given an array of cam_pinfo* elements with the * Heap(index + 1, num_entries) property with index containing * an unsorted entry, output Heap(index, num_entries). */ static void heap_down(cam_pinfo **queue_array, int index, int num_entries) { int child; int parent; parent = index; child = parent << 1; for (; child <= num_entries; child = parent << 1) { if (child < num_entries) { /* child+1 is the right child of parent */ if (queue_cmp(queue_array, child + 1, child) < 0) child++; } /* child is now the least child of parent */ if (queue_cmp(queue_array, parent, child) <= 0) break; swap(queue_array, child, parent); parent = child; } } diff --git a/sys/cam/cam_queue.h b/sys/cam/cam_queue.h index e5928027f030..f1055543e768 100644 --- a/sys/cam/cam_queue.h +++ b/sys/cam/cam_queue.h @@ -1,280 +1,256 @@ /*- * CAM request queue management definitions. * * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1997 Justin T. Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _CAM_CAM_QUEUE_H #define _CAM_CAM_QUEUE_H 1 #ifdef _KERNEL #include #include #include #include /* * This structure implements a heap based priority queue. The queue * assumes that the objects stored in it begin with a cam_qentry * structure holding the priority information used to sort the objects. * This structure is opaque to clients (outside of the XPT layer) to allow * the implementation to change without affecting them. */ struct camq { cam_pinfo **queue_array; int array_size; int entries; u_int32_t generation; u_int32_t qfrozen_cnt; }; TAILQ_HEAD(ccb_hdr_tailq, ccb_hdr); LIST_HEAD(ccb_hdr_list, ccb_hdr); SLIST_HEAD(ccb_hdr_slist, ccb_hdr); struct cam_ccbq { struct camq queue; struct ccb_hdr_tailq queue_extra_head; int queue_extra_entries; int total_openings; int allocated; int dev_openings; int dev_active; }; struct cam_ed; struct cam_devq { struct mtx send_mtx; struct camq send_queue; int send_openings; int send_active; }; struct cam_devq *cam_devq_alloc(int devices, int openings); int cam_devq_init(struct cam_devq *devq, int devices, int openings); void cam_devq_free(struct cam_devq *devq); u_int32_t cam_devq_resize(struct cam_devq *camq, int openings); /* * Allocate a cam_ccb_queue structure and initialize it. */ struct cam_ccbq *cam_ccbq_alloc(int openings); u_int32_t cam_ccbq_resize(struct cam_ccbq *ccbq, int devices); int cam_ccbq_init(struct cam_ccbq *ccbq, int openings); void cam_ccbq_free(struct cam_ccbq *ccbq); void cam_ccbq_fini(struct cam_ccbq *ccbq); /* * Resize a cam queue */ u_int32_t camq_resize(struct camq *queue, int new_size); /* * Initialize a camq structure. Return 0 on success, 1 on failure. */ int camq_init(struct camq *camq, int size); /* * Finialize any internal storage or state of a cam_queue. */ void camq_fini(struct camq *queue); /* * cam_queue_insert: Given a CAM queue with at least one open spot, * insert the new entry maintaining order. */ void camq_insert(struct camq *queue, cam_pinfo *new_entry); /* * camq_remove: Remove and arbitrary entry from the queue maintaining * queue order. */ cam_pinfo *camq_remove(struct camq *queue, int index); #define CAMQ_HEAD 1 /* Head of queue index */ /* Index the first element in the heap */ #define CAMQ_GET_HEAD(camq) ((camq)->queue_array[CAMQ_HEAD]) /* Get the first element priority. */ #define CAMQ_GET_PRIO(camq) (((camq)->entries > 0) ? \ ((camq)->queue_array[CAMQ_HEAD]->priority) : 0) /* * camq_change_priority: Raise or lower the priority of an entry * maintaining queue order. */ void camq_change_priority(struct camq *queue, int index, u_int32_t new_priority); -static __inline int -cam_ccbq_pending_ccb_count(struct cam_ccbq *ccbq); - -static __inline void -cam_ccbq_take_opening(struct cam_ccbq *ccbq); - -static __inline void -cam_ccbq_insert_ccb(struct cam_ccbq *ccbq, union ccb *new_ccb); - -static __inline void -cam_ccbq_remove_ccb(struct cam_ccbq *ccbq, union ccb *ccb); - -static __inline union ccb * -cam_ccbq_peek_ccb(struct cam_ccbq *ccbq, int index); - -static __inline void -cam_ccbq_send_ccb(struct cam_ccbq *queue, union ccb *send_ccb); - -static __inline void -cam_ccbq_ccb_done(struct cam_ccbq *ccbq, union ccb *done_ccb); - -static __inline void -cam_ccbq_release_opening(struct cam_ccbq *ccbq); - static __inline int cam_ccbq_pending_ccb_count(struct cam_ccbq *ccbq) { return (ccbq->queue.entries + ccbq->queue_extra_entries); } static __inline void cam_ccbq_take_opening(struct cam_ccbq *ccbq) { ccbq->allocated++; } static __inline void cam_ccbq_insert_ccb(struct cam_ccbq *ccbq, union ccb *new_ccb) { struct ccb_hdr *old_ccb; struct camq *queue = &ccbq->queue; KASSERT((new_ccb->ccb_h.func_code & XPT_FC_QUEUED) != 0 && (new_ccb->ccb_h.func_code & XPT_FC_USER_CCB) == 0, ("%s: Cannot queue ccb %p func_code %#x", __func__, new_ccb, new_ccb->ccb_h.func_code)); /* * If queue is already full, try to resize. * If resize fail, push CCB with lowest priority out to the TAILQ. */ if (queue->entries == queue->array_size && camq_resize(&ccbq->queue, queue->array_size * 2) != CAM_REQ_CMP) { old_ccb = (struct ccb_hdr *)camq_remove(queue, queue->entries); TAILQ_INSERT_HEAD(&ccbq->queue_extra_head, old_ccb, xpt_links.tqe); old_ccb->pinfo.index = CAM_EXTRAQ_INDEX; ccbq->queue_extra_entries++; } camq_insert(queue, &new_ccb->ccb_h.pinfo); } static __inline void cam_ccbq_remove_ccb(struct cam_ccbq *ccbq, union ccb *ccb) { struct ccb_hdr *cccb, *bccb; struct camq *queue = &ccbq->queue; cam_pinfo *removed_entry __unused; /* If the CCB is on the TAILQ, remove it from there. */ if (ccb->ccb_h.pinfo.index == CAM_EXTRAQ_INDEX) { TAILQ_REMOVE(&ccbq->queue_extra_head, &ccb->ccb_h, xpt_links.tqe); ccb->ccb_h.pinfo.index = CAM_UNQUEUED_INDEX; ccbq->queue_extra_entries--; return; } removed_entry = camq_remove(queue, ccb->ccb_h.pinfo.index); KASSERT(removed_entry == &ccb->ccb_h.pinfo, ("%s: Removed wrong entry from queue (%p != %p)", __func__, removed_entry, &ccb->ccb_h.pinfo)); /* * If there are some CCBs on TAILQ, find the best one and move it * to the emptied space in the queue. */ bccb = TAILQ_FIRST(&ccbq->queue_extra_head); if (bccb == NULL) return; TAILQ_FOREACH(cccb, &ccbq->queue_extra_head, xpt_links.tqe) { if (bccb->pinfo.priority > cccb->pinfo.priority || (bccb->pinfo.priority == cccb->pinfo.priority && GENERATIONCMP(bccb->pinfo.generation, >, cccb->pinfo.generation))) bccb = cccb; } TAILQ_REMOVE(&ccbq->queue_extra_head, bccb, xpt_links.tqe); ccbq->queue_extra_entries--; camq_insert(queue, &bccb->pinfo); } static __inline union ccb * cam_ccbq_peek_ccb(struct cam_ccbq *ccbq, int index) { return((union ccb *)ccbq->queue.queue_array[index]); } static __inline void cam_ccbq_send_ccb(struct cam_ccbq *ccbq, union ccb *send_ccb) { send_ccb->ccb_h.pinfo.index = CAM_ACTIVE_INDEX; ccbq->dev_active++; ccbq->dev_openings--; } static __inline void cam_ccbq_ccb_done(struct cam_ccbq *ccbq, union ccb *done_ccb) { ccbq->dev_active--; ccbq->dev_openings++; } static __inline void cam_ccbq_release_opening(struct cam_ccbq *ccbq) { ccbq->allocated--; } #endif /* _KERNEL */ #endif /* _CAM_CAM_QUEUE_H */