1898b0535SWarner Losh /*-
28b8a9b1dSJustin T. Gibbs * CAM request queue management definitions.
38b8a9b1dSJustin T. Gibbs *
44d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
5bec9534dSPedro F. Giffuni *
68b8a9b1dSJustin T. Gibbs * Copyright (c) 1997 Justin T. Gibbs.
78b8a9b1dSJustin T. Gibbs * All rights reserved.
88b8a9b1dSJustin T. Gibbs *
98b8a9b1dSJustin T. Gibbs * Redistribution and use in source and binary forms, with or without
108b8a9b1dSJustin T. Gibbs * modification, are permitted provided that the following conditions
118b8a9b1dSJustin T. Gibbs * are met:
128b8a9b1dSJustin T. Gibbs * 1. Redistributions of source code must retain the above copyright
138b8a9b1dSJustin T. Gibbs * notice, this list of conditions, and the following disclaimer,
148b8a9b1dSJustin T. Gibbs * without modification, immediately at the beginning of the file.
158b8a9b1dSJustin T. Gibbs * 2. The name of the author may not be used to endorse or promote products
168b8a9b1dSJustin T. Gibbs * derived from this software without specific prior written permission.
178b8a9b1dSJustin T. Gibbs *
188b8a9b1dSJustin T. Gibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
198b8a9b1dSJustin T. Gibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
208b8a9b1dSJustin T. Gibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
218b8a9b1dSJustin T. Gibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
228b8a9b1dSJustin T. Gibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
238b8a9b1dSJustin T. Gibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
248b8a9b1dSJustin T. Gibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
258b8a9b1dSJustin T. Gibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
268b8a9b1dSJustin T. Gibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
278b8a9b1dSJustin T. Gibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
288b8a9b1dSJustin T. Gibbs * SUCH DAMAGE.
298b8a9b1dSJustin T. Gibbs */
308b8a9b1dSJustin T. Gibbs
318b8a9b1dSJustin T. Gibbs #ifndef _CAM_CAM_QUEUE_H
328b8a9b1dSJustin T. Gibbs #define _CAM_CAM_QUEUE_H 1
338b8a9b1dSJustin T. Gibbs
34c4473420SPeter Wemm #ifdef _KERNEL
358b8a9b1dSJustin T. Gibbs
36227d67aaSAlexander Motin #include <sys/lock.h>
37227d67aaSAlexander Motin #include <sys/mutex.h>
388b8a9b1dSJustin T. Gibbs #include <sys/queue.h>
3983c5d981SAlexander Motin #include <cam/cam.h>
408b8a9b1dSJustin T. Gibbs
418b8a9b1dSJustin T. Gibbs /*
428b8a9b1dSJustin T. Gibbs * This structure implements a heap based priority queue. The queue
438b8a9b1dSJustin T. Gibbs * assumes that the objects stored in it begin with a cam_qentry
448b8a9b1dSJustin T. Gibbs * structure holding the priority information used to sort the objects.
458b8a9b1dSJustin T. Gibbs * This structure is opaque to clients (outside of the XPT layer) to allow
468b8a9b1dSJustin T. Gibbs * the implementation to change without affecting them.
478b8a9b1dSJustin T. Gibbs */
488b8a9b1dSJustin T. Gibbs struct camq {
498b8a9b1dSJustin T. Gibbs cam_pinfo **queue_array;
508b8a9b1dSJustin T. Gibbs int array_size;
518b8a9b1dSJustin T. Gibbs int entries;
52*7af2f2c8SWarner Losh uint32_t generation;
53*7af2f2c8SWarner Losh uint32_t qfrozen_cnt;
548b8a9b1dSJustin T. Gibbs };
558b8a9b1dSJustin T. Gibbs
56e3975643SJake Burkholder TAILQ_HEAD(ccb_hdr_tailq, ccb_hdr);
57e3975643SJake Burkholder LIST_HEAD(ccb_hdr_list, ccb_hdr);
58e3975643SJake Burkholder SLIST_HEAD(ccb_hdr_slist, ccb_hdr);
598b8a9b1dSJustin T. Gibbs
608b8a9b1dSJustin T. Gibbs struct cam_ccbq {
618b8a9b1dSJustin T. Gibbs struct camq queue;
62ea541bfdSAlexander Motin struct ccb_hdr_tailq queue_extra_head;
63ea541bfdSAlexander Motin int queue_extra_entries;
64227d67aaSAlexander Motin int total_openings;
65959ec258SAlexander Motin int allocated;
668b8a9b1dSJustin T. Gibbs int dev_openings;
678b8a9b1dSJustin T. Gibbs int dev_active;
688b8a9b1dSJustin T. Gibbs };
698b8a9b1dSJustin T. Gibbs
708b8a9b1dSJustin T. Gibbs struct cam_ed;
718b8a9b1dSJustin T. Gibbs
728b8a9b1dSJustin T. Gibbs struct cam_devq {
73227d67aaSAlexander Motin struct mtx send_mtx;
748b8a9b1dSJustin T. Gibbs struct camq send_queue;
758b8a9b1dSJustin T. Gibbs int send_openings;
768b8a9b1dSJustin T. Gibbs int send_active;
778b8a9b1dSJustin T. Gibbs };
788b8a9b1dSJustin T. Gibbs
798b8a9b1dSJustin T. Gibbs struct cam_devq *cam_devq_alloc(int devices, int openings);
808b8a9b1dSJustin T. Gibbs
818b8a9b1dSJustin T. Gibbs int cam_devq_init(struct cam_devq *devq, int devices,
828b8a9b1dSJustin T. Gibbs int openings);
838b8a9b1dSJustin T. Gibbs
848b8a9b1dSJustin T. Gibbs void cam_devq_free(struct cam_devq *devq);
858b8a9b1dSJustin T. Gibbs
86*7af2f2c8SWarner Losh uint32_t cam_devq_resize(struct cam_devq *camq, int openings);
878b8a9b1dSJustin T. Gibbs
888b8a9b1dSJustin T. Gibbs /*
898b8a9b1dSJustin T. Gibbs * Allocate a cam_ccb_queue structure and initialize it.
908b8a9b1dSJustin T. Gibbs */
918b8a9b1dSJustin T. Gibbs struct cam_ccbq *cam_ccbq_alloc(int openings);
928b8a9b1dSJustin T. Gibbs
93*7af2f2c8SWarner Losh uint32_t cam_ccbq_resize(struct cam_ccbq *ccbq, int devices);
948b8a9b1dSJustin T. Gibbs
958b8a9b1dSJustin T. Gibbs int cam_ccbq_init(struct cam_ccbq *ccbq, int openings);
968b8a9b1dSJustin T. Gibbs
978b8a9b1dSJustin T. Gibbs void cam_ccbq_free(struct cam_ccbq *ccbq);
988b8a9b1dSJustin T. Gibbs
9920a7933fSAlexander Motin void cam_ccbq_fini(struct cam_ccbq *ccbq);
10020a7933fSAlexander Motin
1018b8a9b1dSJustin T. Gibbs /*
1028b8a9b1dSJustin T. Gibbs * Resize a cam queue
1038b8a9b1dSJustin T. Gibbs */
104*7af2f2c8SWarner Losh uint32_t camq_resize(struct camq *queue, int new_size);
1058b8a9b1dSJustin T. Gibbs
1068b8a9b1dSJustin T. Gibbs /*
1078b8a9b1dSJustin T. Gibbs * Initialize a camq structure. Return 0 on success, 1 on failure.
1088b8a9b1dSJustin T. Gibbs */
1098b8a9b1dSJustin T. Gibbs int camq_init(struct camq *camq, int size);
1108b8a9b1dSJustin T. Gibbs
1118b8a9b1dSJustin T. Gibbs /*
1128b8a9b1dSJustin T. Gibbs * Finialize any internal storage or state of a cam_queue.
1138b8a9b1dSJustin T. Gibbs */
1148b8a9b1dSJustin T. Gibbs void camq_fini(struct camq *queue);
1158b8a9b1dSJustin T. Gibbs
1168b8a9b1dSJustin T. Gibbs /*
1178b8a9b1dSJustin T. Gibbs * cam_queue_insert: Given a CAM queue with at least one open spot,
1188b8a9b1dSJustin T. Gibbs * insert the new entry maintaining order.
1198b8a9b1dSJustin T. Gibbs */
1208b8a9b1dSJustin T. Gibbs void camq_insert(struct camq *queue, cam_pinfo *new_entry);
1218b8a9b1dSJustin T. Gibbs
1228b8a9b1dSJustin T. Gibbs /*
1238b8a9b1dSJustin T. Gibbs * camq_remove: Remove and arbitrary entry from the queue maintaining
1248b8a9b1dSJustin T. Gibbs * queue order.
1258b8a9b1dSJustin T. Gibbs */
1268b8a9b1dSJustin T. Gibbs cam_pinfo *camq_remove(struct camq *queue, int index);
1275a526431SJustin T. Gibbs #define CAMQ_HEAD 1 /* Head of queue index */
1285a526431SJustin T. Gibbs
1295a526431SJustin T. Gibbs /* Index the first element in the heap */
1305a526431SJustin T. Gibbs #define CAMQ_GET_HEAD(camq) ((camq)->queue_array[CAMQ_HEAD])
1318b8a9b1dSJustin T. Gibbs
13283c5d981SAlexander Motin /* Get the first element priority. */
13383c5d981SAlexander Motin #define CAMQ_GET_PRIO(camq) (((camq)->entries > 0) ? \
13483c5d981SAlexander Motin ((camq)->queue_array[CAMQ_HEAD]->priority) : 0)
13583c5d981SAlexander Motin
1368b8a9b1dSJustin T. Gibbs /*
1378b8a9b1dSJustin T. Gibbs * camq_change_priority: Raise or lower the priority of an entry
1388b8a9b1dSJustin T. Gibbs * maintaining queue order.
1398b8a9b1dSJustin T. Gibbs */
1408b8a9b1dSJustin T. Gibbs void camq_change_priority(struct camq *queue, int index,
141*7af2f2c8SWarner Losh uint32_t new_priority);
1428b8a9b1dSJustin T. Gibbs
1438b8a9b1dSJustin T. Gibbs static __inline int
cam_ccbq_pending_ccb_count(struct cam_ccbq * ccbq)1448b8a9b1dSJustin T. Gibbs cam_ccbq_pending_ccb_count(struct cam_ccbq *ccbq)
1458b8a9b1dSJustin T. Gibbs {
146ea541bfdSAlexander Motin return (ccbq->queue.entries + ccbq->queue_extra_entries);
1478b8a9b1dSJustin T. Gibbs }
1488b8a9b1dSJustin T. Gibbs
1498b8a9b1dSJustin T. Gibbs static __inline void
cam_ccbq_take_opening(struct cam_ccbq * ccbq)1508b8a9b1dSJustin T. Gibbs cam_ccbq_take_opening(struct cam_ccbq *ccbq)
1518b8a9b1dSJustin T. Gibbs {
152959ec258SAlexander Motin
153959ec258SAlexander Motin ccbq->allocated++;
1548b8a9b1dSJustin T. Gibbs }
1558b8a9b1dSJustin T. Gibbs
1561268d481SAlexander Motin static __inline void
cam_ccbq_insert_ccb(struct cam_ccbq * ccbq,union ccb * new_ccb)1578b8a9b1dSJustin T. Gibbs cam_ccbq_insert_ccb(struct cam_ccbq *ccbq, union ccb *new_ccb)
1588b8a9b1dSJustin T. Gibbs {
159ea541bfdSAlexander Motin struct ccb_hdr *old_ccb;
160ea541bfdSAlexander Motin struct camq *queue = &ccbq->queue;
161ea541bfdSAlexander Motin
162f30cad33SMark Johnston KASSERT((new_ccb->ccb_h.func_code & XPT_FC_QUEUED) != 0 &&
163f30cad33SMark Johnston (new_ccb->ccb_h.func_code & XPT_FC_USER_CCB) == 0,
164f30cad33SMark Johnston ("%s: Cannot queue ccb %p func_code %#x", __func__, new_ccb,
165f30cad33SMark Johnston new_ccb->ccb_h.func_code));
166f30cad33SMark Johnston
167ea541bfdSAlexander Motin /*
168ea541bfdSAlexander Motin * If queue is already full, try to resize.
169ea541bfdSAlexander Motin * If resize fail, push CCB with lowest priority out to the TAILQ.
170ea541bfdSAlexander Motin */
171ea541bfdSAlexander Motin if (queue->entries == queue->array_size &&
172ea541bfdSAlexander Motin camq_resize(&ccbq->queue, queue->array_size * 2) != CAM_REQ_CMP) {
173ea541bfdSAlexander Motin old_ccb = (struct ccb_hdr *)camq_remove(queue, queue->entries);
174ea541bfdSAlexander Motin TAILQ_INSERT_HEAD(&ccbq->queue_extra_head, old_ccb,
175ea541bfdSAlexander Motin xpt_links.tqe);
176ea541bfdSAlexander Motin old_ccb->pinfo.index = CAM_EXTRAQ_INDEX;
177ea541bfdSAlexander Motin ccbq->queue_extra_entries++;
178ea541bfdSAlexander Motin }
179ea541bfdSAlexander Motin
180ea541bfdSAlexander Motin camq_insert(queue, &new_ccb->ccb_h.pinfo);
1818b8a9b1dSJustin T. Gibbs }
1828b8a9b1dSJustin T. Gibbs
1831268d481SAlexander Motin static __inline void
cam_ccbq_remove_ccb(struct cam_ccbq * ccbq,union ccb * ccb)1848b8a9b1dSJustin T. Gibbs cam_ccbq_remove_ccb(struct cam_ccbq *ccbq, union ccb *ccb)
1858b8a9b1dSJustin T. Gibbs {
186ea541bfdSAlexander Motin struct ccb_hdr *cccb, *bccb;
187ea541bfdSAlexander Motin struct camq *queue = &ccbq->queue;
188f30cad33SMark Johnston cam_pinfo *removed_entry __unused;
189ea541bfdSAlexander Motin
190ea541bfdSAlexander Motin /* If the CCB is on the TAILQ, remove it from there. */
191ea541bfdSAlexander Motin if (ccb->ccb_h.pinfo.index == CAM_EXTRAQ_INDEX) {
192ea541bfdSAlexander Motin TAILQ_REMOVE(&ccbq->queue_extra_head, &ccb->ccb_h,
193ea541bfdSAlexander Motin xpt_links.tqe);
194ea541bfdSAlexander Motin ccb->ccb_h.pinfo.index = CAM_UNQUEUED_INDEX;
195ea541bfdSAlexander Motin ccbq->queue_extra_entries--;
196ea541bfdSAlexander Motin return;
197ea541bfdSAlexander Motin }
198ea541bfdSAlexander Motin
199f30cad33SMark Johnston removed_entry = camq_remove(queue, ccb->ccb_h.pinfo.index);
200f30cad33SMark Johnston KASSERT(removed_entry == &ccb->ccb_h.pinfo,
201f30cad33SMark Johnston ("%s: Removed wrong entry from queue (%p != %p)", __func__,
202f30cad33SMark Johnston removed_entry, &ccb->ccb_h.pinfo));
203ea541bfdSAlexander Motin
204ea541bfdSAlexander Motin /*
205ea541bfdSAlexander Motin * If there are some CCBs on TAILQ, find the best one and move it
206ea541bfdSAlexander Motin * to the emptied space in the queue.
207ea541bfdSAlexander Motin */
208ea541bfdSAlexander Motin bccb = TAILQ_FIRST(&ccbq->queue_extra_head);
209ea541bfdSAlexander Motin if (bccb == NULL)
210ea541bfdSAlexander Motin return;
211ea541bfdSAlexander Motin TAILQ_FOREACH(cccb, &ccbq->queue_extra_head, xpt_links.tqe) {
212ea541bfdSAlexander Motin if (bccb->pinfo.priority > cccb->pinfo.priority ||
213ea541bfdSAlexander Motin (bccb->pinfo.priority == cccb->pinfo.priority &&
214ea541bfdSAlexander Motin GENERATIONCMP(bccb->pinfo.generation, >,
215ea541bfdSAlexander Motin cccb->pinfo.generation)))
216ea541bfdSAlexander Motin bccb = cccb;
217ea541bfdSAlexander Motin }
218ea541bfdSAlexander Motin TAILQ_REMOVE(&ccbq->queue_extra_head, bccb, xpt_links.tqe);
219ea541bfdSAlexander Motin ccbq->queue_extra_entries--;
220ea541bfdSAlexander Motin camq_insert(queue, &bccb->pinfo);
2218b8a9b1dSJustin T. Gibbs }
2228b8a9b1dSJustin T. Gibbs
2238b8a9b1dSJustin T. Gibbs static __inline union ccb *
cam_ccbq_peek_ccb(struct cam_ccbq * ccbq,int index)2248b8a9b1dSJustin T. Gibbs cam_ccbq_peek_ccb(struct cam_ccbq *ccbq, int index)
2258b8a9b1dSJustin T. Gibbs {
2268b8a9b1dSJustin T. Gibbs return((union ccb *)ccbq->queue.queue_array[index]);
2278b8a9b1dSJustin T. Gibbs }
2288b8a9b1dSJustin T. Gibbs
2298b8a9b1dSJustin T. Gibbs static __inline void
cam_ccbq_send_ccb(struct cam_ccbq * ccbq,union ccb * send_ccb)2308b8a9b1dSJustin T. Gibbs cam_ccbq_send_ccb(struct cam_ccbq *ccbq, union ccb *send_ccb)
2318b8a9b1dSJustin T. Gibbs {
2328b8a9b1dSJustin T. Gibbs
2338b8a9b1dSJustin T. Gibbs send_ccb->ccb_h.pinfo.index = CAM_ACTIVE_INDEX;
2348b8a9b1dSJustin T. Gibbs ccbq->dev_active++;
2358b8a9b1dSJustin T. Gibbs ccbq->dev_openings--;
2368b8a9b1dSJustin T. Gibbs }
2378b8a9b1dSJustin T. Gibbs
2388b8a9b1dSJustin T. Gibbs static __inline void
cam_ccbq_ccb_done(struct cam_ccbq * ccbq,union ccb * done_ccb)2398b8a9b1dSJustin T. Gibbs cam_ccbq_ccb_done(struct cam_ccbq *ccbq, union ccb *done_ccb)
2408b8a9b1dSJustin T. Gibbs {
2411f1158b2SAlexander Motin
2428b8a9b1dSJustin T. Gibbs ccbq->dev_active--;
2438b8a9b1dSJustin T. Gibbs ccbq->dev_openings++;
2448b8a9b1dSJustin T. Gibbs }
2458b8a9b1dSJustin T. Gibbs
2468b8a9b1dSJustin T. Gibbs static __inline void
cam_ccbq_release_opening(struct cam_ccbq * ccbq)2478b8a9b1dSJustin T. Gibbs cam_ccbq_release_opening(struct cam_ccbq *ccbq)
2488b8a9b1dSJustin T. Gibbs {
249959ec258SAlexander Motin
250959ec258SAlexander Motin ccbq->allocated--;
2518b8a9b1dSJustin T. Gibbs }
2528b8a9b1dSJustin T. Gibbs
253c4473420SPeter Wemm #endif /* _KERNEL */
2548b8a9b1dSJustin T. Gibbs #endif /* _CAM_CAM_QUEUE_H */
255