/*
 * nasd_ioqueue_cscan.c
 *
 * CSCAN queueing for NASD drive
 *
 * Authors: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#include <nasd/nasd_options.h>
#include <nasd/nasd_drive_options.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_freelist.h>
#include <nasd/nasd_itypes.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_cache.h>
#include <nasd/nasd_common.h>
#include <nasd/nasd_timer.h>
#include <nasd/nasd_security.h>
#include <nasd/nasd_sys.h>
#include <nasd/nasd_drive_io.h>
#include <nasd/nasd_ioqueue.h>

#if NASD_DRIVE_IOQUEUE_INCLUDE_CSCAN > 0

typedef struct nasd_ioqueue_cscan_queue_s {
  nasd_odc_ent_t   pend;  /* ring of pending I/O */
  nasd_odc_ent_t  *right; /* to right of arm */
} nasd_ioqueue_cscan_queue_t;

nasd_ioqueue_cscan_queue_t nasd_ioqueue_cscan_queues[NASD_IO_PRI_NUM];

nasd_sectno_t nasd_od_io_last_dispatched_sect;

/*
 * Validate that "right" pointer for queue structure is in the
 * correct place.
 */
#if 0
#define NASD_CSQ_ASSERT_ARM(_queue_) { \
 NASD_ASSERT( \
 ((_queue_)->right != NULL) && \
 ((((_queue_)->right == &(_queue_)->pend) && \
   (((_queue_)->pend.inext == &(_queue_)->pend) || \
     ((_queue_)->pend.iprev->real_sectno < nasd_od_io_last_dispatched_sect))) \
  || \
  (((_queue_)->right != &(_queue_)->pend) && \
   ((_queue_)->right->io_flags & NASD_CI_IOQ) && \
   ((_queue_)->right->real_sectno >= nasd_od_io_last_dispatched_sect) && \
   ((((_queue_)->right->iprev->real_sectno < nasd_od_io_last_dispatched_sect)) \
    || \
    ((_queue_)->right->iprev == &(_queue_)->pend))))); \
}
#else
#define NASD_CSQ_ASSERT_ARM(_queue_) { \
  if ((_queue_)->right == NULL) { \
    nasd_printf("%d queue->right == NULL!\n", __LINE__); \
    NASD_PANIC(); \
  } \
  if ((_queue_)->right == &(_queue_)->pend) { \
    if ((_queue_)->pend.inext == &(_queue_)->pend) { \
      if ((_queue_)->pend.iprev != &(_queue_)->pend) { \
        nasd_printf("%d queue empty forward but not backward!\n", __LINE__); \
        NASD_PANIC(); \
      } \
    } \
    else { \
      if ((_queue_)->pend.iprev->real_sectno >= \
        nasd_od_io_last_dispatched_sect) \
      { \
        nasd_printf("%d arm at end of queue, should be to left of prev io!\n", \
          __LINE__); \
        NASD_PANIC(); \
      } \
    } \
  } \
  else { \
    if (!((_queue_)->right->io_flags & NASD_CI_IOQ)) { \
      nasd_printf("%d arm points at nonenqueued I/O!\n", __LINE__); \
      NASD_PANIC(); \
    } \
    if ((_queue_)->right->real_sectno < nasd_od_io_last_dispatched_sect) { \
      nasd_printf("%d arm right points at block that should be left!\n", \
        __LINE__); \
      NASD_PANIC(); \
    } \
    if ((_queue_)->right->iprev != &(_queue_)->pend) { \
      if ((_queue_)->right->iprev->real_sectno >= \
        nasd_od_io_last_dispatched_sect) \
      { \
        nasd_printf("%d arm points too far to the right!\n", __LINE__); \
        NASD_PANIC(); \
      } \
    } \
  } \
}
#endif

/*
 * Initialize cscan queueing subsystem.
 * Called at start-of-day.
 */
nasd_status_t
nasd_ioqueue_cscan_init(
  nasd_od_config_t  *config)
{
  nasd_ioqueue_cscan_queue_t *queue;
  int i;

  nasd_od_io_last_dispatched_sect = 0;

  for(i=0;i<NASD_IO_PRI_NUM;i++) {
    queue = &nasd_ioqueue_cscan_queues[i];
    queue->pend.iopri = i;
    queue->pend.inext = queue->pend.iprev = &queue->pend;
    queue->right = &queue->pend;
    NASD_CSQ_ASSERT_ARM(queue);
  }

  return(NASD_SUCCESS);
}

/*
 * Enqueue a sorted list of I/Os. Called with
 * I/O lock held.
 */
nasd_status_t
nasd_ioqueue_cscan_enq_sorted(
  nasd_odc_ent_t  *entlist,
  int              dir,
  int              pri,
  char            *file,
  int              line)
{
  nasd_ioqueue_cscan_queue_t *queue;
  nasd_odc_ent_t *ent, *ex, *prev;

  NASD_ASSERT(entlist != NULL);
  queue = &nasd_ioqueue_cscan_queues[pri];

  /*
   * If queue is empty, do the easy thing.
   */
  if (queue->pend.inext == &queue->pend) {
    /*
     * While we iterate, queue->right perpetually points to
     * the last item we've found to the _left_ of the arm.
     * After the loop, we notch it one to the right, and voila!
     */
    queue->right = &queue->pend;
    /* grab beginning of list */
    queue->pend.inext = entlist;
    for(prev=&queue->pend,ent=entlist;ent;prev=ent,ent=ent->inext) {
      /* fixup prev pointers */
      NASD_ASSERT(prev != NULL);
      ent->iprev = prev;
      if (ent->real_sectno <= nasd_od_io_last_dispatched_sect) {
        queue->right = ent;
      }
      NASD_ASSERT(!(ent->io_flags&(NASD_CI_DISPATCH|NASD_CI_IOQ)));
      ent->io_flags |= NASD_CI_IOQ;
      ent->iodir = dir;
      ent->iopri = pri;
    }
    /* tail list into ring */
    prev->inext = &queue->pend;
    queue->pend.iprev = prev;
    NASD_ASSERT(queue->pend.iprev->inext == &queue->pend);
    /* fixup arm hint pointer (see above) */
    queue->right = queue->right->inext;
    NASD_CSQ_ASSERT_ARM(queue);

    /* issue I/Os if we need to */
    nasd_od_io_launch_as_necessary();

    return(NASD_SUCCESS);
  }

  /*
   * Find first element in the queue that we should
   * enqueue BEFORE. First, look for a good place to
   * start.
   */
  ex = queue->pend.inext;
  if ((ex != &queue->pend) && (queue->right != &queue->pend)) {
    if (entlist->real_sectno > ex->real_sectno) {
      if (entlist->real_sectno > queue->right->real_sectno) {
        ex = queue->right;
      }
      else {
        NASD_ASSERT(queue->right->iprev != &queue->pend);
        if (entlist->real_sectno > queue->right->iprev->real_sectno) {
          ex = queue->right->iprev;
        }
      }
    }
  }
  /*
   * We now know that we can ignore everything before ex
   * in looking for a place to insert.
   */
  NASD_ASSERT((ex->iprev == &queue->pend) ||
    (ex->iprev->real_sectno < entlist->real_sectno));

  while(entlist) {
    ent = entlist;
    entlist = entlist->inext;
    for(;
      ((ex!=&queue->pend)&&(ent->real_sectno > ex->real_sectno));
      ex=ex->inext)
    {
      NASD_ASSERT(ent->real_sectno != ex->real_sectno);
    }
    NASD_ASSERT((ex == &queue->pend) || (ent->real_sectno < ex->real_sectno));
    /* insert before ex */
    ent->inext = ex;
    NASD_ASSERT(ex->iprev != NULL);
    ent->iprev = ex->iprev;
    ent->iprev->inext = ent;
    ent->inext->iprev = ent;
    NASD_ASSERT(!(ent->io_flags&(NASD_CI_DISPATCH|NASD_CI_IOQ)));
    ent->io_flags |= NASD_CI_IOQ;
    ent->iodir = dir;
    ent->iopri = pri;

    /* update arm hint */
    if (ent->real_sectno >= nasd_od_io_last_dispatched_sect) {
      if ((queue->right == &queue->pend) ||
          (queue->right->real_sectno > ent->real_sectno))
      {
        queue->right = ent;
      }
    }
  }

  NASD_CSQ_ASSERT_ARM(queue);

  /* issue I/Os if we need to */
  nasd_od_io_launch_as_necessary();

  return(NASD_SUCCESS);
}

/*
 * Enqueue an unsorted list of I/Os. Called with
 * I/O lock held.
 */
nasd_status_t
nasd_ioqueue_cscan_enq(
  nasd_odc_ent_t  *entlist,
  int              dir,
  int              pri,
  char            *file,
  int              line)
{
  nasd_ioqueue_cscan_queue_t *queue;
  nasd_odc_ent_t *ent, *ex;

  NASD_ASSERT(entlist != NULL);
  queue = &nasd_ioqueue_cscan_queues[pri];

  while(entlist) {
    ent = entlist;
    entlist = entlist->inext;

    /*
     * Find first element in the queue that we should
     * enqueue BEFORE. First, look for a good place to
     * start.
     */
    ex = queue->pend.inext;
    if ((ex != &queue->pend) && (queue->right != &queue->pend)) {
      if (ent->real_sectno > ex->real_sectno) {
        if (ent->real_sectno > queue->right->real_sectno) {
          ex = queue->right;
        }
        else {
          NASD_ASSERT(queue->right->iprev != &queue->pend);
          if (ent->real_sectno > queue->right->iprev->real_sectno) {
            ex = queue->right->iprev;
          }
        }
      }
    }

    /*
     * We now know that we can ignore everything before ex
     * in looking for a place to insert.
     */
    NASD_ASSERT((ex->iprev == &queue->pend) ||
      (ex->iprev->real_sectno < ent->real_sectno));

    for(;
      ((ex!=&queue->pend)&&(ent->real_sectno > ex->real_sectno));
      ex=ex->inext)
    {
      NASD_ASSERT(ent->real_sectno != ex->real_sectno);
    }
    NASD_ASSERT((ex == &queue->pend) || (ent->real_sectno < ex->real_sectno));

    /* insert before ex */
    ent->inext = ex;
    NASD_ASSERT(ex->iprev != NULL);
    ent->iprev = ex->iprev;
    ent->iprev->inext = ent;
    ent->inext->iprev = ent;
    NASD_ASSERT(!(ent->io_flags&(NASD_CI_DISPATCH|NASD_CI_IOQ)));
    ent->io_flags |= NASD_CI_IOQ;
    ent->iodir = dir;
    ent->iopri = pri;

    /* update arm hint */
    if (ent->real_sectno >= nasd_od_io_last_dispatched_sect) {
      if ((queue->right == &queue->pend) ||
          (queue->right->real_sectno > ent->real_sectno))
      {
        queue->right = ent;
      }
    }
  }

  NASD_CSQ_ASSERT_ARM(queue);

  /*
   * Issue I/Os if necessary.
   */
  nasd_od_io_launch_as_necessary();

  return(NASD_SUCCESS);
}

/*
 * Dequeue next valid I/O (or I/O chain). Called with
 * I/O lock held.
 *
 * Here, we perform deep ritual magic. First, we find the
 * highest priority queue with I/O pending in it. Then we
 * grab the next I/O after the arm in it. If there's no
 * I/O after the arm, we go back to the beginning, resetting
 * our notion of the arm position to zero. Once there,
 * we cluster backwards until we either get to the arm or
 * an alignment boundary, and forwards until we hit an alignment
 * boundary or clustering limit. When we cluster, we will consider
 * I/O from lower priority levels as well as eligible for
 * clustering.
 */
nasd_status_t
nasd_ioqueue_cscan_deq_next(
  nasd_odc_ent_t  **entlist,
  char             *file,
  int               line)
{
  int qpri, i, coalesced, max_coalesce, dir, cluster_forward, cluster_back;
  nasd_odc_ent_t *base, *first, *last, *prevs[NASD_IO_PRI_NUM], *ent;
  nasd_ioqueue_cscan_queue_t *queue, *lq;
  nasd_blkno_t next_sectno;
#if NASD_IOQUEUE_TIMERS > 0
  nasd_timespec_t ts;
  nasd_timer_t tm;
#endif /* NASD_IOQUEUE_TIMERS > 0 */

  *entlist = NULL;

#if NASD_IOQUEUE_TIMERS > 0
  NASD_TM_START(&tm);
#endif /* NASD_IOQUEUE_TIMERS > 0 */

  if (nasd_drive_io_max_coalesce)
    max_coalesce = NASD_MIN(nasd_drive_io_max_coalesce, NASD_IO_MAX_COALESCE);
  else
    max_coalesce = NASD_IO_MAX_COALESCE;

  qpri = (-1);

  for(i=0;i<NASD_IO_PRI_NUM;i++) {
    queue = &nasd_ioqueue_cscan_queues[i];
    if (queue->pend.inext == &queue->pend) {
      continue;
    }
    qpri = i;
    break;
  }
  if (qpri < 0) {
    /* queues are empty */
    *entlist = NULL;
    first = NULL;
    goto done_dequeue;
  }

  /*
   * queue (#qpri) is highest priority nonempty queue
   */

  /*
   * Set up base to point at first I/O in our priority
   * level after the arm (resetting to beginning if we
   * have I/O at this priority level but nothing after
   * the arm).
   */

  if (queue->right == &queue->pend) {
    /*
     * Elevator to ground floor.
     */
    nasd_od_io_last_dispatched_sect = 0;
    base = queue->pend.inext;
    for(i=0;i<NASD_IO_PRI_NUM;i++) {
      lq = &nasd_ioqueue_cscan_queues[i];
      lq->right = lq->pend.inext;
      NASD_CSQ_ASSERT_ARM(lq);
    }
  }
  else {
    base = queue->right;
  }

  /*
   * Set up prevs array to point to last item
   * before arm at each level.
   */
  for(i=qpri;i<NASD_IO_PRI_NUM;i++) {
    prevs[i] = nasd_ioqueue_cscan_queues[i].right->iprev;
    NASD_ASSERT(prevs[i] != NULL);
  }

  if (base == queue->right)
    queue->right = base->inext;
  base->inext->iprev = base->iprev;
  base->iprev->inext = base->inext;
  base->inext = base->iprev = NULL;

  last = first = base;
  dir = base->iodir;

  base->io_flags &= ~NASD_CI_IOQ;
  base->io_flags |= NASD_CI_DISPATCH;

  cluster_forward = 1;
  cluster_back = 1;
  coalesced = 1;

  /*
   * Determine if we should cluster at all.
   */

  next_sectno = first->real_sectno;
  if (coalesced >= max_coalesce) {
    if (dir == NASD_U_READ) {
      NASD_IO_INC_STAT(coalesce_stop_max_read);
    }
    else {
      NASD_IO_INC_STAT(coalesce_stop_max_write);
    }
    cluster_forward = 0;
    cluster_back = 0;
  }
  if ((next_sectno % nasd_drive_io_cluster_align) == 0) {
    if (dir == NASD_U_READ) {
      NASD_IO_INC_STAT(coalesce_stop_align_read);
    }
    else {
      NASD_IO_INC_STAT(coalesce_stop_align_write);
    }
    cluster_back = 0;
  }
  next_sectno += NASD_OD_SECTORS_PER_BLK;
  if ((next_sectno % nasd_drive_io_cluster_align) == 0) {
    if (dir == NASD_U_READ) {
      NASD_IO_INC_STAT(coalesce_stop_align_read);
    }
    else {
      NASD_IO_INC_STAT(coalesce_stop_align_write);
    }
    cluster_forward = 0;
  }
  if (base->real_sectno < NASD_OD_SECTORS_PER_BLK) {
    /* beginning of disk */
    cluster_back = 0;
  }

  /*
   * Cluster backward until we hit an alignment boundary
   * or an access size limit, if we should cluster backward.
   */

  next_sectno = base->real_sectno - NASD_OD_SECTORS_PER_BLK;

  while(cluster_back && (next_sectno > nasd_od_io_last_dispatched_sect)) {
    for(i=qpri;i<NASD_IO_PRI_NUM;i++) {
      lq = &nasd_ioqueue_cscan_queues[i];
      if ((prevs[i] != &lq->pend)
        && (prevs[i]->real_sectno == next_sectno))
      {
        /* this physically precedes the next block in the I/O */

        if (prevs[i]->iodir != dir) {
          continue;
        }

        if (coalesced >= max_coalesce) {
          if (dir == NASD_U_READ) {
            NASD_IO_INC_STAT(coalesce_stop_max_read);
          }
          else {
            NASD_IO_INC_STAT(coalesce_stop_max_write);
          }
          cluster_forward = 0;
          cluster_back = 0;
          break;
        }

        /*
         * Okay to cluster this in.
         */

        ent = prevs[i];
        prevs[i] = ent->iprev;
        ent->iprev->inext = ent->inext;
        ent->inext->iprev = ent->iprev;
        ent->iprev = NULL;
        ent->inext = first;
        first->iprev = ent;
        first = ent;
        coalesced++;
        ent->io_flags &= ~NASD_CI_IOQ;
        ent->io_flags |= NASD_CI_DISPATCH;

        next_sectno -= NASD_OD_SECTORS_PER_BLK;
        if ((next_sectno % nasd_drive_io_cluster_align) == 0) {
          if (dir == NASD_U_READ) {
            NASD_IO_INC_STAT(coalesce_stop_align_read);
          }
          else {
            NASD_IO_INC_STAT(coalesce_stop_align_write);
          }
          cluster_back = 0;
        }

        break;
      }
    }
    if (i >= NASD_IO_PRI_NUM) {
      if (dir == NASD_U_READ) {
        NASD_IO_INC_STAT(coalesce_stop_contig_read);
      }
      else {
        NASD_IO_INC_STAT(coalesce_stop_contig_write);
      }
      break;
    }
  }

  /*
   * Cluster forward until we hit an alignment boundary
   * or an access size limit, if we should cluster forward.
   */

  next_sectno = base->real_sectno + NASD_OD_SECTORS_PER_BLK;

  while(cluster_forward) {
    for(i=qpri;i<NASD_IO_PRI_NUM;i++) {
      lq = &nasd_ioqueue_cscan_queues[i];
      if ((lq->right != &lq->pend)
        && (lq->right->real_sectno == next_sectno))
      {
        /* this physically follows the previous block in the I/O */

        if (lq->right->iodir != dir) {
          continue;
        }

        if (coalesced >= max_coalesce) {
          if (dir == NASD_U_READ) {
            NASD_IO_INC_STAT(coalesce_stop_max_read);
          }
          else {
            NASD_IO_INC_STAT(coalesce_stop_max_write);
          }
          cluster_forward = 0;
          cluster_back = 0;
          break;
        }

        /*
         * Okay to cluster this in.
         */

        ent = lq->right;
        lq->right = ent->inext;
        ent->iprev->inext = ent->inext;
        ent->inext->iprev = ent->iprev;
        ent->iprev = last;
        ent->inext = NULL;
        last->inext = ent;
        last = ent;
        coalesced++;
        ent->io_flags &= ~NASD_CI_IOQ;
        ent->io_flags |= NASD_CI_DISPATCH;

        NASD_ASSERT(ent->iopri == i);
        NASD_CSQ_ASSERT_ARM(lq);

        next_sectno += NASD_OD_SECTORS_PER_BLK;
        if ((next_sectno % nasd_drive_io_cluster_align) == 0) {
          if (dir == NASD_U_READ) {
            NASD_IO_INC_STAT(coalesce_stop_align_read);
          }
          else {
            NASD_IO_INC_STAT(coalesce_stop_align_write);
          }
          cluster_forward = 0;
        }

        break;
      }
    }
    if (i >= NASD_IO_PRI_NUM) {
      if (dir == NASD_U_READ) {
        NASD_IO_INC_STAT(coalesce_stop_contig_read);
      }
      else {
        NASD_IO_INC_STAT(coalesce_stop_contig_write);
      }
      break;
    }
  }

  *entlist = first;

  nasd_od_io_last_dispatched_sect = last->real_sectno
    + NASD_OD_SECTORS_PER_BLK - 1;

  for(i=0;i<NASD_IO_PRI_NUM;i++) {
    lq = &nasd_ioqueue_cscan_queues[i];
    while(lq->right != &lq->pend) {
      NASD_ASSERT(lq->right->io_flags & NASD_CI_IOQ);
      if (lq->right->real_sectno >= nasd_od_io_last_dispatched_sect)
        break;
      lq->right = lq->right->inext;
    }
    NASD_CSQ_ASSERT_ARM(lq);
  }

done_dequeue:

#if NASD_IOQUEUE_TIMERS > 0
  NASD_TM_STOP(&tm);
  NASD_TM_ELAPSED_TS(&tm, &ts);
  NASD_ATOMIC_TIMESPEC_ADD(&nasd_drive_ioqueue_stats.io_coalesce_time, &ts);
  if (first) {
    NASD_IO_TM_LAUNCH(first);
  }
#endif /* NASD_IOQUEUE_TIMERS > 0 */

  for(i=0;i<NASD_IO_PRI_NUM;i++) {
    NASD_CSQ_ASSERT_ARM(&nasd_ioqueue_cscan_queues[i]);
  }

  return(NASD_SUCCESS);
}

/*
 * Dequeue a specific entry. Called with I/O lock held.
 */
nasd_status_t
nasd_ioqueue_cscan_deq_ent(
  nasd_odc_ent_t  *ent,
  char            *file,
  int              line)
{
  nasd_ioqueue_cscan_queue_t *queue;

  NASD_ASSERT((ent->io_flags&(NASD_CI_IOQ|NASD_CI_DISPATCH))
    == NASD_CI_DISPATCH);

  queue = &nasd_ioqueue_cscan_queues[ent->iopri];

  if (queue->right == ent)
    queue->right = ent->inext;

  ent->iprev->inext = ent->inext;
  ent->inext->iprev = ent->iprev;
  /* NASD_CI_IOQ already not set (see assert above) */

  nasd_od_io_last_dispatched_sect = ent->real_sectno
    + (NASD_OD_SECTORS_PER_BLK - 1);

  NASD_CSQ_ASSERT_ARM(queue);

  return(NASD_SUCCESS);
}

/*
 * If enqueued, make sure that ent is enqueued at a priority
 * level of at least newpri. Called with I/O lock held.
 */
nasd_status_t
nasd_ioqueue_cscan_try_raise_pri(
  nasd_odc_ent_t  *ent,
  int              newpri)
{
  nasd_ioqueue_cscan_queue_t *queue;
  nasd_status_t rc;
  int oldpri;

  if (ent->iopri <= newpri)
    return(NASD_SUCCESS);
  NASD_ASSERT((ent->io_flags&(NASD_CI_IOQ|NASD_CI_DISPATCH)) == NASD_CI_IOQ);

  oldpri = ent->iopri;

  queue = &nasd_ioqueue_cscan_queues[oldpri];

  ent->iprev->inext = ent->inext;
  ent->inext->iprev = ent->iprev;

  if (queue->right == ent)
    queue->right = queue->right->inext;

  ent->io_flags &= ~NASD_CI_IOQ;
  ent->inext = ent->iprev = NULL;

  rc = nasd_ioqueue_cscan_enq_sorted(ent, ent->iodir, newpri, __FILE__, __LINE__);
  if (rc) {
    NASD_PANIC();
  }

  NASD_CSQ_ASSERT_ARM(&nasd_ioqueue_cscan_queues[oldpri]);
  NASD_CSQ_ASSERT_ARM(&nasd_ioqueue_cscan_queues[newpri]);

  return(NASD_SUCCESS);
}

/*
 * The I/O module is informing us that a synchronous I/O
 * is launching. Called with I/O lock held.
 */
nasd_status_t
nasd_ioqueue_cscan_sync_launched(
  nasd_sectno_t  sector)
{
  nasd_ioqueue_cscan_queue_t *queue;
  int i;

  /*
   * Update nasd_od_io_last_dispatched_sect.
   *
   * For each priority level, first check if we've
   * gone past the end. Then scan forward either from
   * where we are or from the beginning (don't scan
   * backward- gone to 0 is a common case).
   */

  nasd_od_io_last_dispatched_sect = sector;

  for(i=0;i<NASD_IO_PRI_NUM;i++) {
    queue = &nasd_ioqueue_cscan_queues[i];
    if (queue->pend.inext == &queue->pend) {
      /* nothing in queue */
      NASD_CSQ_ASSERT_ARM(queue);
      continue;
    }
    else {
      if (queue->pend.iprev->real_sectno < sector) {
        /* new position is past everything in the queue */
        queue->right = &queue->pend;
      }
      else {
        /*
         * I used to try to optimize here by starting
         * from the old arm position, but that I/O may
         * be dequeued by now, and that could lead
         * to all sorts of corruption.
         */
        queue->right = queue->pend.inext;
        while (queue->right != &queue->pend) {
          if (queue->right->inext == &queue->pend) {
            if (queue->right->real_sectno < sector)
              queue->right = queue->right->inext;
            break;
          }
          if (queue->right->inext->real_sectno >= sector)
            break;
          queue->right = queue->right->inext;
        }
      }
    }
    NASD_CSQ_ASSERT_ARM(queue);
  }

  return(NASD_SUCCESS);
}

#endif /* NASD_DRIVE_IOQUEUE_INCLUDE_CSCAN > 0 */

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
