/*!
  @file           ven542.c
  @author         JoergM
  @brief          Kernel RunTime: Asynchronous Disk I/O
  @see            

\if EMIT_LICENCE

    ========== licence begin  GPL
    Copyright (c) 2001-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end



\endif
*/

#define MOD__ "ven542.c:"

#include "gen00.h"
#include "heo00.h"
#include "hen40.h"
#include "geo50_0.h"
#include "geo54.h"
#include "gen500.h"
#include "gen54.h"

#define        VB_NOTOK                 0
#define        VB_OK                    1

/**************************************************************************/
#    if     SUN_SOL6 || OSF1
/**************************************************************************/
#    include        <aio.h>
#    define         RTE_SIG_AIO SIGUSR1 
/**************************************************************************/
#    endif
/**************************************************************************/

/* exported functions */

int     e54a_install_aio_signal_handler() ;

int     e54_asyn_vblockio         ( struct DOUBLY_LINKED  * request,
                                           tsp00_ErrText             errtext) ;

int     e54_asyn_vasynio          ( struct DOUBLY_LINKED  * request,
                                           tsp00_ErrText             errtext) ;

int     e54_asyn_vasynio_result   ( struct DOUBLY_LINKED  * request,
                                           tsp00_ErrText             errtext) ;

static  int     e54a_sys_asynio_start    ( int                     fd,
                                           tsp2_io_op_code         op_code,
                                           tsp00_Int4                length ,
                                           off_t                   offset, 
                                           char                  * buf,
                                           struct DOUBLY_LINKED  * request,
                                           tsp00_ErrText             errtext) ;

static  int     e54a_sys_asynio_return   ( int                     aio_errno,
                                           void                    *aio_op,
                                           tsp00_Int4                *io_bytes,
                                           tsp00_ErrText             errtext) ;

/* imported functions */
extern void                            e54_int_to_text () ;

/**************************************************************************/
#    if     SUN_SOL6 || OSF1
/**************************************************************************/

/*
 * ===========================================================================
 */

static void    e54a_aioc ( struct TASK_TYPE * tcb , 
                           struct DOUBLY_LINKED * quu )
{
  #undef  MF__
  #define MF__ MOD__"e54a_aioc"
  ten50_UKT_Control *this_ukt = THIS_UKT_CTRL;
  struct IOR_QUEUE_HEAD           * head ;

  DBGIN;

  /*
   *  Enter queue element in AIOC queue
   */

  if ( tcb->ukt != this_ukt )
    {
      MSGCD (( IERR_DISP_ENQUE_WRONG_UKP, "e54_aioc" )) ;
      return ;
    }

  quu->taskid = tcb ;
  if ( tcb->prio_flag || tcb->excl_schachtel )
    tcb->ukt->SelfDispatch ++ ; 

  head = &this_ukt->aioc_queue ;
  head->first->request = quu ;
  head->first          = head->first->next ;
  tcb->is_in_queue     = TASK_IS_IN_IOC_QUEUE ;
  tcb->QueStat.ulUKTQueCount++ ;

  DBG3 (( MF__, "task T%d   ukt tid %ld", tcb->index , tcb->ukt->tid ))

  DBGOUT ;
}

/*
 * ===========================================================================
 */

void	e54a_aio_catch_signal ( int sig,
                                siginfo_t *info ,
                                void      *ignore ) 
{
  #undef  MF__
  #define MF__ MOD__"e54a_aio_catch_signal"
  ten50_UKT_Control *this_ukt = THIS_UKT_CTRL;
  struct TASK_TYPE *tcb ;
  struct DOUBLY_LINKED *request ;
  struct aiocb *aio_op ;

  if ( sig != RTE_SIG_AIO || info->si_code != SI_ASYNCIO )
    {
      if ( sig != RTE_SIG_AIO )
        MSGD ((ERR_AIO_SIG_BAD_SIGNO, sig )) 
      else
        if ( info->si_code != SI_ASYNCIO )
          MSGD ((ERR_AIO_SIG_BAD_SI_CODE, info->si_code, SI_ASYNCIO )) ;
      return ;    
    }
  this_ukt->aio.TotalAIOSig++ ;
  request = ( struct DOUBLY_LINKED * ) info->si_value.sival_ptr ;
  tcb = request->taskid ;
  aio_op     = request->req_type == REQ_ASYNIO 
             ? ( struct aiocb *) request->args.vai_parms.aio_op
             : ( struct aiocb *) tcb->aio_op ;
  if ( tcb && tcb->aio_in_progress &&
      (tcb->aio_errno = aio_error  ( aio_op )) != EINPROGRESS )
    { this_ukt->aio.aio_complete ++;
      /* tcb->aio_in_progress-- ; */
      e54a_aioc ( tcb , request ) ;
#ifdef DEBUG_SIGHANDLER
      DBG3 (( MF__,"signal aio_completed for T%d", tcb->index ));
      DBG3 (( MF__,"ukt %d, aio_in_prog=%d, aio_compl=%d", THREAD_INDEX(this_ukt) ,
          this_ukt->aio.aio_in_progress,  this_ukt->aio.aio_complete )) ;
#endif
    }
  else
    if ( !tcb ) 
      MSGD ((ERR_AIO_SIG_NO_TCB)) 
    else
      if ( !tcb->aio_in_progress ) 
        MSGD ((ERR_AIO_SIG_FOR_NONWAITING_TASK, tcb->index )) 
      else
        if ( tcb->aio_errno == EINPROGRESS )
          MSGD ((ERR_AIO_SIG_BUT_EINPROGRESS , tcb->index )) 
}

/**************************************************************************/
#    endif
/**************************************************************************/

/*
 * ===========================================================================
 */
int  e54a_get_aio_struct_size ()
{
/**************************************************************************/
#if ( OSF1 || SUN_SOL6 )
/**************************************************************************/
return ( sizeof ( struct aiocb ) ) ;
/**************************************************************************/
#else
/**************************************************************************/
return ( 0 ) ;
/**************************************************************************/
#endif
/**************************************************************************/
}

/*
 * ===========================================================================
 */

void  e54a_init_sys_aio_struc () 
{
/**************************************************************************/
#if ( OSF1 || SUN_SOL6 )
/**************************************************************************/
  struct TASK_TYPE *tcb ;
  for ( tcb = KGS->pFirstTaskCtrl ; tcb <= KGS->pLastTaskCtrl ; tcb ++ )
    { struct aiocb *aio_op = ( struct aiocb *) tcb->aio_op ;

      aio_op->aio_sigevent.sigev_notify          = SIGEV_SIGNAL  ; 
      aio_op->aio_sigevent.sigev_signo = RTE_SIG_AIO; /* completion signal */
      aio_op->aio_sigevent.sigev_value.sival_ptr = &(tcb->qa) ; 
                                                   /* REQ_VBLOCKIO */
      if (  tcb->asyn_request )
        { int i ;
          for ( i=0; i < KGS->max_asyn_request; i++ )
            { aio_op = ( struct aiocb *)
                       tcb->asyn_request[i].args.vai_parms.aio_op ;
              aio_op->aio_sigevent.sigev_notify  = SIGEV_SIGNAL  ; 
              aio_op->aio_sigevent.sigev_signo   = RTE_SIG_AIO; 
              aio_op->aio_sigevent.sigev_value.sival_ptr = 
                                       tcb->asyn_request  +i ; /* REQ_ASYNIO */
            }
        }
    }
/**************************************************************************/
#endif
/**************************************************************************/
}

/*
 * ===========================================================================
 */
int e54a_install_aio_signal_handler ()
{
/**************************************************************************/
#if ( OSF1 || SUN_SOL6 )
/**************************************************************************/
struct sigaction sig_act ;
void             e54a_aio_catch_signal () ;

sig_act.sa_handler = e54a_aio_catch_signal ;
sigemptyset(&sig_act.sa_mask ) ;
sigaddset  (&sig_act.sa_mask, RTE_SIG_AIO ) ;
sig_act.sa_flags = SA_SIGINFO ;
if ( sigaction ( RTE_SIG_AIO, &sig_act, 0 ) == -1 )
  return ( FALSE ) ;
else
/**************************************************************************/
#endif
/**************************************************************************/
  return ( TRUE ) ;
}

/*
 * ===========================================================================
 */
static  int     e54a_sys_asynio_start ( int                     fd,
                                        tsp2_io_op_code         op_code,
                                        tsp00_Int4                length ,
                                        off_t                   offset, 
                                        char                  * buf,
                                        struct DOUBLY_LINKED  * request,
                                        tsp00_ErrText             errtext)
{
  #undef  MF__
  #define MF__ MOD__"e54a_sys_asynio_start"
  ten50_UKT_Control *this_ukt = THIS_UKT_CTRL;
  int ret_code = FALSE ;

/**************************************************************************/
#   if ( OSF1 || SUN_SOL6 )
/**************************************************************************/

  struct aiocb *aio_op     = request->req_type == REQ_ASYNIO 
                             ? ( struct aiocb *) request->args.vai_parms.aio_op
                             : ( struct aiocb *) this_ukt->curr_task->aio_op ;
  struct sigevent lio_sigevent = {0,0};     /* sigevent only in aiocb !! */

  DBGIN;
  DBG3 (( MF__,"AIO: fd=%d, mode %s, offset = %d", fd,
          op_code == sp2ioc_read ? "READ" : "WRITE", offset ));

  aio_op->aio_fildes      = fd ;
  aio_op->aio_lio_opcode  = op_code == sp2ioc_read ? LIO_READ : LIO_WRITE ;
  aio_op->aio_nbytes      = length ;
  aio_op->aio_offset      = offset ;
  aio_op->aio_buf         = buf ;
/*
  aio_op->aio_sigevent.sigev_value.sival_ptr = request  ; 
*/
  this_ukt->aio.TotalAIOReq++ ;
  this_ukt->curr_task->aio_in_progress++;
  ret_code = lio_listio ( LIO_NOWAIT, &aio_op, 1, &lio_sigevent );

  DBG3 (( MF__,"ret of lio_listio %d", ret_code ));

  if ( ret_code != 0 )
    {
      if ( op_code == sp2ioc_read )
          MSGD ((ERR_SINGLEIO_ERR_READ, sqlerrs() ))
      else
          MSGD ((ERR_SINGLEIO_ERR_WRITE, sqlerrs() ))

      e54_int_to_text ( errno, errtext );
      ret_code = FALSE ;
      this_ukt->curr_task->aio_in_progress--;
    }
  else
    { 
      ret_code = TRUE ;
      DBG3 (( MF__,"T%d waiting for aio_complete",this_ukt->curr_task->index));
      this_ukt->aio.aio_in_progress ++;
    }
  DBGOUT;
/**************************************************************************/
#endif
/**************************************************************************/
  return (ret_code);
  }


/*
 * ===========================================================================
 */

static  int     e54a_sys_asynio_return (
                        int                     aio_errno,
                        void                    *aio_op,
                        tsp00_Int4                *io_bytes,
                        tsp00_ErrText             errtext)
{
  #undef  MF__
  #define MF__ MOD__"e54a_sys_asynio_return"
  ten50_UKT_Control *this_ukt = THIS_UKT_CTRL;
  int ret_code = FALSE;

   *io_bytes = 0 ;
/**************************************************************************/
#   if ( OSF1 || SUN_SOL6 )
/**************************************************************************/

  this_ukt->aio.aio_in_progress-- ;
  this_ukt->curr_task->aio_in_progress--;
  DBG3 (( MF__,"T%d from disp ... ", this_ukt->curr_task->index));
  if ( aio_errno == 0 )
    { 
      *io_bytes = aio_return ( ( struct aiocb *) aio_op ) ;
      DBG3 (( MF__,"T%d : %d bytes made for io", this_ukt->curr_task->index, *io_bytes));
      ret_code = TRUE ;
    }
  else
    {
      MSGD ((ERR_AIO_ERRNO, aio_errno));
      e54_int_to_text ( aio_errno , errtext );
      ret_code = FALSE ;
    }
  DBGOUT;
/**************************************************************************/
#endif
/**************************************************************************/
  return (ret_code);
}

/*
 * ===========================================================================
 */
int e54_asyn_vblockio  ( struct DOUBLY_LINKED  * request,
                                 tsp00_ErrText             errtext)
{
  #undef  MF__
  #define MF__ MOD__"e54_asyn_vblockio"
  ten50_UKT_Control *this_ukt = THIS_UKT_CTRL;
  int ret = VB_NOTOK, fd ;
  tsp00_Int4  Retries = 0 ;  /* PTS 1106176 */
/**************************************************************************/
#   if ( OSF1 || SUN_SOL6 )
/**************************************************************************/
  tsp00_Int4 io_bytes = 0 ;
  tsp00_Int4 length_wanted = request->args.vbl_parms.buf_count *
                           KGS->dev_blocksize ;
  tsp00_Int4        devno = request->args.vbl_parms.devno ;

  DBGIN ;

  ret = e54_init_self_io ( this_ukt, devno,
                           request->args.vbl_parms.devpno,
                           request->args.vbl_parms.op_code,
                           &fd, errtext ) ;

  if ( ret == VB_OK )
  {
    if ( request->args.vbl_parms.op_code == sp2ioc_read )
       (this_ukt->UKPIO[devno].single_read)++;
    else
       (this_ukt->UKPIO[devno].single_write)++;

    do
    {
      ret = e54a_sys_asynio_start ( fd, request->args.vbl_parms.op_code, 
                                    length_wanted, 
                                    (off_t) KGS->dev_blocksize *
                                     ( (off_t) request->args.vbl_parms.devpno +
                                       (off_t) 1 ) ,
                                    (char *)request->args.vbl_parms.p,
                                    request, errtext) ;

      if ( ret )
        {
/**************************************************************************/
          GOTO_DISP (&this_ukt);
/**************************************************************************/
          ret = e54a_sys_asynio_return ( this_ukt->curr_task->aio_errno,
                                         this_ukt->curr_task->aio_op, &io_bytes, errtext);
          if ( ret )
            if ( io_bytes != length_wanted )
              {
                MSGD ((IERR_SINGLEIO_ERR_IO_CONT, 
                        this_ukt->curr_task->aio_errno, io_bytes));
                e54_int_to_text ( this_ukt->curr_task->aio_errno, errtext );
                ret = VB_NOTOK ;
              }
        }
    }
    while ( ret != VB_OK && ++Retries < NUMBER_OF_IO_RETRIES ); /* PTS 1106175 */
  }

  if ( ret == VB_OK )
    request->args.vbl_parms.ok = TRUE;
/**************************************************************************/
#endif
/**************************************************************************/
  DBGOUT;
  return (ret);
}
  

/*
 * ===========================================================================
 */
int e54_asyn_vasynio  ( struct DOUBLY_LINKED  * request,
                                tsp00_ErrText             errtext)
{
  #undef  MF__
  #define MF__ MOD__"e54_asyn_vasynio"
  ten50_UKT_Control *this_ukt = THIS_UKT_CTRL;
  int ret = FALSE , fd ;
/**************************************************************************/
#   if ( OSF1 || SUN_SOL6 )
/**************************************************************************/
  tsp00_Int4 length_wanted = request->args.vai_parms.block_siz *
                           request->args.vai_parms.block_cnt ;
  tsp00_Int4         devno = request->args.vai_parms.devno ;
  tsp2_io_op_code op_code =  KGS->dev0.dev[devno].asyn_write 
                            ? sp2ioc_write : sp2ioc_read ;

  DBGIN ;

  ret = e54_init_self_io ( this_ukt, devno, request->args.vai_parms.devpno,
                           op_code, &fd, errtext ) ;

  if ( ret == VB_OK )
    {
      if ( op_code == sp2ioc_read )
         (this_ukt->UKPIO[devno].single_read)++;
      else
          (this_ukt->UKPIO[devno].single_write)++;

      ret = e54a_sys_asynio_start ( fd, op_code, length_wanted, 
                                    (off_t) KGS->dev_blocksize *
                                     ( (off_t) request->args.vai_parms.devpno +
                                       (off_t) 1 ) ,
                                    (char *)request->args.vai_parms.block_ptr,
                                    request, errtext) ;

    }

  if ( ret == VB_OK )
    request->args.vai_parms.ok = TRUE;

/**************************************************************************/
#endif
/**************************************************************************/

  DBGOUT;
  return (ret);
}
  
/*
 * ===========================================================================
 */
int e54_asyn_vasynio_result  ( struct DOUBLY_LINKED  * request,
                                       tsp00_ErrText             errtext)
{
  #undef  MF__
  #define MF__ MOD__"e54_asyn_vasynio_result"
  ten50_UKT_Control *this_ukt = THIS_UKT_CTRL;
  int ret = FALSE , fd ;
/**************************************************************************/
#   if ( OSF1 || SUN_SOL6 )
/**************************************************************************/
  tsp00_Int4 io_bytes = 0 ;
  ret = e54a_sys_asynio_return ( this_ukt->curr_task->aio_errno,
                                 request->args.vai_parms.aio_op,
                                 &io_bytes, errtext);
  if ( !(request->args.vai_parms.ok = this_ukt->curr_task->aio_errno == 0 ) )
      request->args.vai_parms.block_cnt = io_bytes / 
                                          request->args.vai_parms.block_siz ;
/**************************************************************************/
#endif
/**************************************************************************/

  DBGOUT;
  return (ret);
}
