** Copyright 2002/03, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the MIT License.
*/
Part of Open SCSI bus manager
Queuing of SCSI request
Some words about queuing, i.e. making sure that no request
gets stuck in some queue forever
As long as the SIM accepts new reqs, XPT doesn't take care of
stuck requests - thus, peripheral drivers should use synced reqs
once in a while (or even always) to force the HBA to finish
previously submitted reqs. This is also important for non-queuing
HBAs as in this case the queuing is done by the SCSI bus manager
which uses elevator sort.
Requests can be blocked by SIM or by the SCSI bus manager on a per-device
or per-bus basis. There are three possible reasons:
1. the hardware queue is too small
- detected by bus manager automatically, or
- detected by SIM which calls "requeue" for affected request
2. SIM blocks bus/device explictely
3. bus manager blocks bus/device explictly (currently used for
ordered requests)
4. the device has queued requests or the bus has waiting devices resp.
The first condition is automatically cleared when a request has
been completed, but can be reset manually by the SIM. In the second and
third cases, the SIM/bus manager must explictely unblock the bus/device.
For easier testing, the lock_count is the sum of the overflow bit, the
SIM lock count and the bus manager lock count. The blocking test is in
scsi_check_enqueue_request() in scsi_io.c.
If a bus/device is blocked or has waiting requests/devices, new requests
are added to a device-specific request queue in an elevator-sort style,
taking care that no ordered requests are overtaken. Exceptions are
requeued request and autosense-requests, which are added first. Each bus
has a queue of non-blocked devices that have waiting requests. If a
device is to be added to this queue, it is always appended at tail
to ensure fair processing.
*/
#include "scsi_internal.h"
#include "queuing.h"
static void scsi_insert_new_request( scsi_device_info *device,
scsi_ccb *new_request )
{
scsi_ccb *first, *last, *before, *next;
SHOW_FLOW( 3, "inserting new_request=%p, pos=%" B_PRId64, new_request,
new_request->sort );
first = device->queued_reqs;
if( first == NULL ) {
SHOW_FLOW0( 1, "no other queued request" );
scsi_add_req_queue_first( new_request );
return;
}
SHOW_FLOW( 3, "first=%p, pos=%" B_PRId64 ", last_pos=%" B_PRId64,
first, first->sort, device->last_sort );
if( new_request->ordered ) {
SHOW_FLOW0( 1, "adding synced request to tail" );
scsi_add_req_queue_last( new_request );
return;
}
if( new_request->sort < 0 ) {
SHOW_FLOW0( 1, "adding unsortable request to tail" );
scsi_add_req_queue_last( new_request );
return;
}
last = first->prev;
if( (device->last_sort <= new_request->sort &&
new_request->sort <= first->sort) ||
(first->sort < device->last_sort &&
new_request->sort <= first->sort) )
{
SHOW_FLOW0( 3, "trying to insert req at head of device req queue" );
for( before = last; !before->ordered; ) {
before = before->prev;
if( before == last )
break;
}
if( !before->ordered ) {
SHOW_FLOW0( 1, "scheduled request in front of all other reqs of device" );
scsi_add_req_queue_first( new_request );
return;
} else
SHOW_FLOW0( 1, "req would bypass ordered request" );
}
if( last->ordered ) {
SHOW_FLOW0( 1, "last entry is ordered, adding new request as last" );
scsi_add_req_queue_last( new_request );
return;
}
SHOW_FLOW0( 3, "performing insertion sort" );
for( before = last->prev, next = last;
before != last && !before->ordered;
next = before, before = before->prev )
{
if( before->sort <= new_request->sort && new_request->sort <= next->sort )
break;
}
if( before->ordered ) {
SHOW_FLOW0( 1, "overtaking ordered request in sorting - adding as last" );
scsi_add_req_queue_last( new_request );
return;
}
SHOW_FLOW( 1, "inserting after %p (pos=%" B_PRId64 ") and before %p (pos=%" B_PRId64 ")",
before, before->sort, next, next->sort );
new_request->next = next;
new_request->prev = before;
next->prev = new_request;
before->next = new_request;
}
void scsi_add_queued_request( scsi_ccb *request )
{
scsi_device_info *device = request->device;
SHOW_FLOW0( 3, "" );
request->state = SCSI_STATE_QUEUED;
scsi_insert_new_request( device, request );
scsi_ccb *tmp = device->queued_reqs;
dprintf( "pos=%lld, to_insert=%lld; ", device->last_sort,
request->sort );
do {
dprintf( "%lld, %s", tmp->sort, tmp->next == device->queued_reqs ? "\n" : "" );
tmp = tmp->next;
} while( tmp != device->queued_reqs );
}*/
if( device->lock_count == 0 ) {
SHOW_FLOW0( 3, "mark device as waiting" );
scsi_add_device_queue_last( device );
}
}
void scsi_add_queued_request_first( scsi_ccb *request )
{
scsi_device_info *device = request->device;
SHOW_FLOW0( 3, "" );
request->state = SCSI_STATE_QUEUED;
scsi_add_req_queue_first( request );
if( device->lock_count == 0 ) {
SHOW_FLOW0( 3, "mark device as waiting" );
scsi_add_device_queue_first( device );
}
}
void scsi_remove_queued_request( scsi_ccb *request )
{
scsi_remove_req_queue( request );
if( request->device->queued_reqs == NULL )
scsi_remove_device_queue( request->device );
}
static void scsi_unblock_bus_int( scsi_bus_info *bus, bool by_SIM )
{
bool was_servicable, start_retry;
SHOW_FLOW0( 3, "" );
mutex_lock( &bus->mutex );
was_servicable = scsi_can_service_bus( bus );
scsi_unblock_bus_noresume( bus, by_SIM );
start_retry = !was_servicable && scsi_can_service_bus( bus );
mutex_unlock( &bus->mutex );
if( start_retry )
release_sem( bus->start_service );
}
void scsi_unblock_bus( scsi_bus_info *bus )
{
scsi_unblock_bus_int( bus, true );
}
static void scsi_unblock_device_int( scsi_device_info *device, bool by_SIM )
{
scsi_bus_info *bus = device->bus;
bool was_servicable, start_retry;
SHOW_FLOW0( 3, "" );
mutex_lock( &bus->mutex );
was_servicable = scsi_can_service_bus( bus );
scsi_unblock_device_noresume( device, by_SIM );
if( device->lock_count == 0 && device->queued_reqs != NULL )
scsi_add_device_queue_last( device );
start_retry = !was_servicable && scsi_can_service_bus( bus );
mutex_unlock( &bus->mutex );
if( start_retry )
release_sem( bus->start_service );
}
void scsi_unblock_device( scsi_device_info *device )
{
return scsi_unblock_device_int( device, true );
}
void scsi_cont_send_bus( scsi_bus_info *bus )
{
bool was_servicable, start_retry;
SHOW_FLOW0( 3, "" );
mutex_lock( &bus->mutex );
was_servicable = scsi_can_service_bus( bus );
scsi_clear_bus_overflow( bus );
start_retry = !was_servicable && scsi_can_service_bus( bus );
mutex_unlock( &bus->mutex );
if( start_retry )
release_sem_etc( bus->start_service, 1, 0 );
}
void scsi_cont_send_device( scsi_device_info *device )
{
scsi_bus_info *bus = device->bus;
bool was_servicable, start_retry;
SHOW_FLOW0( 3, "" );
mutex_lock( &bus->mutex );
was_servicable = scsi_can_service_bus( bus );
if( device->sim_overflow ) {
device->sim_overflow = false;
--device->lock_count;
if( device->lock_count == 0 && device->queued_reqs != NULL )
scsi_add_device_queue_last( device );
}
scsi_clear_bus_overflow( bus );
start_retry = !was_servicable && scsi_can_service_bus( bus );
mutex_unlock( &bus->mutex );
if( start_retry )
release_sem_etc( bus->start_service, 1, 0 );
}
static void scsi_block_bus_int( scsi_bus_info *bus, bool by_SIM )
{
SHOW_FLOW0( 3, "" );
mutex_lock( &bus->mutex );
scsi_block_bus_nolock( bus, by_SIM );
mutex_unlock( &bus->mutex );
}
void scsi_block_bus( scsi_bus_info *bus )
{
return scsi_block_bus_int( bus, true );
}
static void scsi_block_device_int( scsi_device_info *device, bool by_SIM )
{
scsi_bus_info *bus = device->bus;
SHOW_FLOW0( 3, "" );
mutex_lock( &bus->mutex );
scsi_block_device_nolock( device, by_SIM );
scsi_remove_device_queue( device );
mutex_unlock( &bus->mutex );
}
void scsi_block_device( scsi_device_info *device )
{
return scsi_block_device_int( device, true );
}