#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 );
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 );
}