/*-
* Copyright (c)2004,2005,2006,2008,2009,2011,2012 YAMAMOTO Takashi,
* 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.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 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.
*/
#if defined(PRIOCSCAN_USE_GLOBAL_POSITION)
/*
* XXX using "global" head position can reduce positioning time
* when switching between queues.
* although it might affect against fairness.
*/
struct cscan_key bq_lastkey;
#endif
};
/*
* how many requests to serve when having pending requests on other queues.
*
* XXX tune
* be careful: while making these values larger likely
* increases the total throughput, it can also increase latencies
* for some workloads.
*/
const int priocscan_burst[] = {
64, 16, 4
};
/*
* find the highest priority non-empty queue.
*/
pq = &q->bq_queue[0];
epq = pq + PRIOCSCAN_NQUEUE;
for (; pq < epq; pq++) {
if (!cscan_empty(&pq->q_queue)) {
break;
}
}
if (pq == epq) {
/*
* all our queues are empty. there's nothing to serve.
*/
return NULL;
}
first = pq;
/*
* scan the rest of queues.
*
* if we have two or more non-empty queues, we serve the highest
* priority one with non-zero burst count.
*/
single = true;
for (npq = pq + 1; npq < epq; npq++) {
if (!cscan_empty(&npq->q_queue)) {
/*
* we found another non-empty queue.
* it means that a queue needs to consume its burst
* count to be served.
*/
single = false;
/*
* check if our current candidate queue has already
* exhausted its burst count.
*/
if (pq->q_burst > 0) {
break;
}
pq = npq;
}
}
if (single) {
/*
* there's only a non-empty queue.
* just serve it without consuming its burst count.
*/
KASSERT(pq == first);
} else {
/*
* there are two or more non-empty queues.
*/
if (pq->q_burst == 0) {
/*
* no queues can be served because they have already
* exhausted their burst count.
*/
unsigned int i;
#ifdef DEBUG
for (i = 0; i < PRIOCSCAN_NQUEUE; i++) {
pq = &q->bq_queue[i];
if (!cscan_empty(&pq->q_queue) && pq->q_burst) {
panic("%s: inconsist", __func__);
}
}
#endif /* DEBUG */
/*
* reset burst counts.
*/
if (remove) {
for (i = 0; i < PRIOCSCAN_NQUEUE; i++) {
pq = &q->bq_queue[i];
pq->q_burst = priocscan_burst[i];
}
}
/*
* serve the highest priority non-empty queue.
*/
pq = first;
}
/*
* consume the burst count.
*
* XXX account only by number of requests. is it good enough?
*/
if (remove) {
KASSERT(pq->q_burst > 0);
pq->q_burst--;
}
}
/*
* finally, get a request from the selected queue.
*/
KDASSERT(!cscan_empty(&pq->q_queue));
bp = cscan_get(&pq->q_queue, remove,
#if defined(PRIOCSCAN_USE_GLOBAL_POSITION)
&q->bq_lastkey
#else /* defined(PRIOCSCAN_USE_GLOBAL_POSITION) */
&pq->q_queue.cq_lastkey
#endif /* defined(PRIOCSCAN_USE_GLOBAL_POSITION) */
);
KDASSERT(bp != NULL);
KDASSERT(&pq->q_queue == bufq_priocscan_selectqueue(q, bp));
for (i = 0; i < PRIOCSCAN_NQUEUE; i++) {
struct cscan_queue * const cq = &q->bq_queue[i].q_queue;
struct buf *it;
/*
* XXX probably could be faster but the cancel functionality
* is not widely used anyway.
*/
RB_TREE_FOREACH(it, &cq->cq_buffers) {
if (it == bp) {
rb_tree_remove_node(&cq->cq_buffers, bp);
return bp;
}
}
}
return NULL;
}