/*
 * nasd_cheops_cl_vnasd.c
 *
 * Cheops client clerk service module exporting a virtual NASD interface
 *
 * Authors: Khalil Amiri, CMU SCS/ECE, July 18 1997
 *          Sean Levy, CMU SCS, July 1999
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,1997,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 <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <malloc.h>
#include <signal.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>

#include <nasd/nasd_mem.h>
#include <nasd/nasd_pdrive_client.h>
#include <nasd/nasd_threadstuff.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_cheops_types.h>
#include <nasd/nasd_cheops_cache.h>
#include <nasd/nasd_cheops_common.h>
#include <nasd/nasd_cheops_raidmap.h>
#include <nasd/nasd_cheops_mgr.h>
#include <nasd/nasd_cheops_ios.h>
#include <nasd/nasd_cheops_client_internal.h>
#include "nasd_cheops_cl_vnasd.h"
#include "nasd_cheops_time.h"

/* Possible debugging */
/* #define NASD_OPERATIONS_DEBUG */
/* #define NASD_GETATTR_DEBUG */
/* #define NASD_READ_DEBUG */
/* #define NASD_WRITE_DEBUG */

#define _NASD_CHEOPS_CL_RECORD_TIME 0
#define _NASD_CHEOPS_CL_DEBUG 0

/* XXX You really don't want to be doing floating point in the kernel, and
   _NASD_CHEOPS_CL_RECORD_TIME => floating point (for now). SNL */

#ifdef _KERNEL
# ifdef _NASD_CHEOPS_CL_RECORD_TIME
#  undef _NASD_CHEOPS_CL_RECORD_TIME
# endif /* _NASD_CHEOPS_CL_RECORD_TIME */
#endif /* _KERNEL */

/* Nasty but necessary globals */
extern _nasd_cheops_cache_t *_nasd_cheops_bs_handle_cachep;
extern _nasd_cheops_cache_t *_nasd_cheops_dr_handle_cachep;
extern _nasd_cheops_mgr_handles_t _nasd_cheops_mgr_handles;

/* proto */ 
extern int   _nasd_cheops_map_access(NASD_layout_t *, nasd_offset_t,
                                     nasd_len_t, nasd_byte_t *, nasd_asm_t *);
extern int   _nasd_cheops_raid0_read(nasd_asm_t *, nasd_len_t *);
extern int  _nasd_cheops_raid0_write(nasd_asm_t *, nasd_len_t *);
extern int   _nasd_cheops_raid1_read(nasd_asm_t *, nasd_len_t *);
extern int  _nasd_cheops_raid1_write(nasd_asm_t *, nasd_len_t *);
extern int   _nasd_cheops_raid5_read(nasd_asm_t *, nasd_len_t *);
extern int  _nasd_cheops_raid5_write(nasd_asm_t *, nasd_len_t *);
extern int  _nasd_cheops_noraid_read(nasd_asm_t *, nasd_len_t *);
extern int _nasd_cheops_noraid_write(nasd_asm_t *, nasd_len_t *);

/* The client clerk exports a basic NASD interface to upper layers.    */
/* To distinguish them from the physical NASD objects, we call         */
/* the objects exported by the clerk, cheops objects or byte-segments. */
/* No support is planned for conditional atomic operations though.     */
/* These extensions are provided only by the physical NASDs.           */

void 
_nasd_null_cl(
  nasd_cheops_handle_t   client_handle, 
  nasd_status_t         *nasd_status,
  nasd_rpc_status_t        *op_status)
{
  *nasd_status = NASD_SUCCESS;
	*op_status = 0; /* always successful */
}

/* Attributes are stored in the parity object for RAID levels 1 */
/* and 4(5). For RAID level 0, the attributes are stored in the */
/* object containing the first stripe unit */

/* XXX This routine is only called from nasd_cheops_cl_test.c, which is
   not built as part of the normal make process.  Therefore, I choose
   not to care about it.  SNL */
void 
_nasd_getattr_cl(
  nasd_cheops_handle_t   client_handle,
  nasd_cookie_t          in_cookie,
  nasd_identifier_t      in_identifier,
  nasd_partnum_t         in_partnum,
  nasd_attribute_t      *out_attribute,
  nasd_status_t         *nasd_status,
  nasd_rpc_status_t     *op_status)
{
  int rc=0, lcol;
	nasd_drive_handle_t  fh=NULL, lh=NULL;
	nasd_identifier_t  lni;
	nasd_identifier_t  fni;
	nasd_disk_ident_t  fdi;
	nasd_disk_ident_t  ldi;
	nasd_partnum_t     fpartnum, lpartnum;
	nasd_attribute_t   *fout_attr;
	nasd_attribute_t   *lout_attr;
	_nasd_cheops_attribute_t ca;
  nasd_cheops_bs_handle_t bs_handle;
	_nasd_cheops_cache_key_t in_key;
	_nasd_cheops_dr_handle_t dr_handle;
	_nasd_cheops_aggr_io_t  *pio;       /* parent io */
  _nasd_cheops_io_t *fio, *lio;
	nasd_key_t req_key;

	NASD_Malloc(fout_attr, sizeof(nasd_attribute_t), (nasd_attribute_t *));
  if (fout_attr == NULL) {
	  (cheops_Msg "malloc failed ..\n");
	  *nasd_status = ENOMEM;
	  return;
	}
	NASD_Malloc(lout_attr, sizeof(nasd_attribute_t), (nasd_attribute_t *));
  if (lout_attr == NULL) {
	  (cheops_Msg "malloc failed ..\n");
	  *nasd_status = ENOMEM;
	  return;
	}
	NASD_Malloc(pio, sizeof(_nasd_cheops_aggr_io_t), (_nasd_cheops_aggr_io_t *));
  if (pio == NULL) {
	  (cheops_Msg "malloc failed ..\n");
	  *nasd_status = ENOMEM;
	  return;
	}

	rc =  nasd_mutex_init(&(pio->flag_mutex));
  if (rc) {
    (cheops_Msg "Error: can not initialize mutex\n");
    *nasd_status = rc;
	  return;
  }
  rc = nasd_cond_init(&(pio->ios_all_done));
  if (rc) {
    (cheops_Msg "Error: can not initialize condition variable\n");
    *nasd_status = rc;
	  return;
  }
	NASD_Malloc(fio, sizeof(_nasd_cheops_io_t), (_nasd_cheops_io_t *));
	if (fio == NULL) {
	  (cheops_Msg "malloc failed ..\n");
	  *nasd_status = ENOMEM;
	  return;
	} 
	NASD_Malloc(lio, sizeof(_nasd_cheops_io_t), (_nasd_cheops_io_t *));
	if (fio == NULL) {
	  (cheops_Msg "malloc failed ..\n");
	  *nasd_status = ENOMEM;
	  return;
	} 
	pio->first_io = fio; fio->cnext = lio; lio->cnext = NULL; 
	/* TODO!! null terminate raid0 queue also*/

	fio->parent_io = pio; lio->parent_io = pio;
  pio->num_ios = 2;
	pio->num_done = 0;

  /* lookup the stripe map of the byte-segment in the cache */
	in_key.object_id = 0;
	in_key.record_id = in_identifier;
  rc = _nasd_cheops_cache_read(_nasd_cheops_bs_handle_cachep, 
                               in_key, (caddr_t) &bs_handle);
	if (rc) {
	  *nasd_status = EINVAL;
	  return;
	}

	/* Fork off two getattributes */
	/* this is necessary if we want cheops layer to be transparent  */
	/* we have to use one fs-specific space for internal attributes,*/
	/* and another for the higher level, right?                     */

	lcol = bs_handle.num_col - 1;
	ldi = bs_handle.identifier_list[lcol].disk_identifier;
	lni = bs_handle.identifier_list[lcol].nasd_identifier;
	fdi = bs_handle.identifier_list[0].disk_identifier;
  fni = bs_handle.identifier_list[0].nasd_identifier;

	in_key.object_id = 0;
	in_key.record_id = fdi;
	rc = _nasd_cheops_cache_read(_nasd_cheops_dr_handle_cachep, 
                               in_key, (caddr_t) &dr_handle);
	fh = dr_handle.h;
	fpartnum = dr_handle.partnum;
	
	in_key.object_id = 0;
	in_key.record_id = ldi;
	rc = _nasd_cheops_cache_read(_nasd_cheops_dr_handle_cachep, 
                               in_key, (caddr_t) &dr_handle);
	lh = dr_handle.h;
	lpartnum = dr_handle.partnum;

	fio->type = _NASD_CHEOPS_GETATTR_IO;
	bcopy(req_key, fio->req_key, sizeof(nasd_key_t));
	fio->ni = fni;
	fio->di = fdi;
	fio->databuf = (nasd_byte_t *) fout_attr;

	lio->type = _NASD_CHEOPS_GETATTR_IO;
	bcopy(req_key, lio->req_key, sizeof(nasd_key_t));
	lio->ni = lni;
	lio->di = ldi;
	lio->databuf = (nasd_byte_t *) lout_attr;

	_nasd_cheops_enq_io(fio);   
	_nasd_cheops_enq_io(lio);  

  NASD_LOCK_MUTEX(pio->flag_mutex);
  while ( pio->num_done < pio->num_ios ) {
    NASD_WAIT_COND(pio->ios_all_done, pio->flag_mutex);
  }
  NASD_UNLOCK_MUTEX(pio->flag_mutex);

	bcopy((char*)(fout_attr->fs_specific),
        (char*)(out_attribute->fs_specific), NASD_FS_SPECIFIC_INFO_SIZE);

	bcopy((char*)(lout_attr->fs_specific),
        (char*)&ca, sizeof(_nasd_cheops_attribute_t));

	out_attribute->object_len = ca.object_len;
	out_attribute->block_size = ca.block_size;
	out_attribute->object_create_time = ca.object_create_time;
	out_attribute->object_modify_time = ca.object_modify_time;
	out_attribute->attr_modify_time = ca.attr_modify_time;
	out_attribute->fs_object_modify_time = ca.object_modify_time;
	out_attribute->fs_attr_modify_time = ca.attr_modify_time;
	
	rc = pio->nasd_status + pio->status;
	if(rc)
	  (cheops_Msg "getattr failed! (nasd_status/op_status) = (%d/%d)\n",
     pio->nasd_status, pio->status);

	NASD_Free(fout_attr, sizeof(nasd_attribute_t));
	NASD_Free(lout_attr, sizeof(nasd_attribute_t));
	_nasd_cheops_io_freeall(pio);
}

/* XXX This routine is called exactly ZERO times from anywhere in NASD
   that I can find.  It also isn't called from siolib.  I thus choose
   not to care about it.  SNL */
void 
_nasd_setattr_cl(
  nasd_cheops_handle_t   client_handle,
  nasd_cookie_t          in_cookie,
  nasd_identifier_t      in_identifier,
  nasd_attribute_t      *in_attribute,
  nasd_fieldmask_t       in_fieldmask,
  nasd_partnum_t         in_partnum,
  nasd_guard_t           in_guard,
  nasd_attribute_t      *out_attribute,
  nasd_status_t         *nasd_status,
  nasd_rpc_status_t     *op_status)
{
  int rc=0, lcol;
	nasd_drive_handle_t  fh=NULL, lh=NULL;
	nasd_identifier_t  lni;
	nasd_identifier_t  fni;
	nasd_disk_ident_t  fdi;
	nasd_disk_ident_t  ldi;
	nasd_partnum_t     fpartnum, lpartnum;
	nasd_attribute_t   *fin_attr;
	nasd_attribute_t   *lin_attr;
	_nasd_cheops_attribute_t ca;
  nasd_cheops_bs_handle_t bs_handle;
	_nasd_cheops_cache_key_t in_key;
	_nasd_cheops_dr_handle_t dr_handle;
	_nasd_cheops_aggr_io_t  *pio;       /* parent io */
  _nasd_cheops_io_t *fio, *lio;
	nasd_key_t req_key;

	NASD_Malloc(fin_attr, sizeof(nasd_attribute_t), (nasd_attribute_t *));
  if (fin_attr == NULL) {
	  (cheops_Msg "malloc failed ..\n");
	  *nasd_status = ENOMEM;
	  return;
	}
	NASD_Malloc(lin_attr, sizeof(nasd_attribute_t), (nasd_attribute_t *));
  if (lin_attr == NULL) {
	  (cheops_Msg "malloc failed ..\n");
	  *nasd_status = ENOMEM;
	  return;
	}
	NASD_Malloc(pio, sizeof(_nasd_cheops_aggr_io_t), (_nasd_cheops_aggr_io_t *));
  if (pio == NULL) {
	  (cheops_Msg "malloc failed ..\n");
	  *nasd_status = ENOMEM;
	  return;
	}

	rc =  nasd_mutex_init(&(pio->flag_mutex));
  if (rc) {
    (cheops_Msg "CHEOPS Error: can not initialize mutex\n");
    *nasd_status = rc;
	  return;
  }
  rc = nasd_cond_init(&(pio->ios_all_done));
  if (rc) {
    (cheops_Msg "CHEOPS Error: can not initialize condition variable\n");
    *nasd_status = rc;
	  return;
  }
	NASD_Malloc(fio, sizeof(_nasd_cheops_io_t), (_nasd_cheops_io_t *));
	if (fio == NULL) {
	  (cheops_Msg "malloc failed ..\n");
	  *nasd_status = ENOMEM;
	  return;
	} 
	NASD_Malloc(lio, sizeof(_nasd_cheops_io_t), (_nasd_cheops_io_t *));
	if (fio == NULL) {
	  (cheops_Msg "malloc failed ..\n");
	  *nasd_status = ENOMEM;
	  return;
	} 
	pio->first_io = fio; fio->cnext = lio; lio->cnext = NULL; 
	fio->parent_io = pio; lio->parent_io = pio;

  pio->num_ios = 2;
	pio->num_done = 0;
  /* lookup the stripe map of the byte-segment in the cache */
	in_key.object_id = 0;
	in_key.record_id = in_identifier;
  rc = _nasd_cheops_cache_read(_nasd_cheops_bs_handle_cachep, 
                               in_key, (caddr_t) &bs_handle);
	if (rc) {
	  *nasd_status = EINVAL;
	  return;
	}

	/* Fork off two setattributes */
	/* this is necessary if we want cheops layer to be transparent  */
	/* we have to use one fs-specific space for internal attributes,*/
	/* and another for the higher level, right?                     */

	lcol = bs_handle.num_col - 1;
	ldi = bs_handle.identifier_list[lcol].disk_identifier;
	lni = bs_handle.identifier_list[lcol].nasd_identifier;
	fdi = bs_handle.identifier_list[0].disk_identifier;
  fni = bs_handle.identifier_list[0].nasd_identifier;

	in_key.object_id = 0;
	in_key.record_id = fdi;
	rc = _nasd_cheops_cache_read(_nasd_cheops_dr_handle_cachep, 
                               in_key, (caddr_t) &dr_handle);
	fh = dr_handle.h;
	fpartnum = dr_handle.partnum;
	
	in_key.object_id = 0;
	in_key.record_id = ldi;
	rc = _nasd_cheops_cache_read(_nasd_cheops_dr_handle_cachep, 
                               in_key, (caddr_t) &dr_handle);
	lh = dr_handle.h;
	lpartnum = dr_handle.partnum;

	fio->type = _NASD_CHEOPS_SETATTR_IO;
	memcpy(req_key, fio->req_key, sizeof(nasd_key_t));
	fio->ni = fni;
	fio->di = fdi;
	fio->databuf = (nasd_byte_t *) fin_attr;

	lio->type = _NASD_CHEOPS_SETATTR_IO;
	memcpy(req_key, lio->req_key, sizeof(nasd_key_t));
	lio->ni = lni;
	lio->di = ldi;
	lio->databuf = (nasd_byte_t *) lin_attr;

	/* split the attributes between the two fs specific objects */
	bcopy((char*)in_attribute->fs_specific,
        (char *)fin_attr->fs_specific, NASD_FS_SPECIFIC_INFO_SIZE);
	ca.object_len = in_attribute->object_len;
	ca.block_size = in_attribute->block_size;
	ca.object_create_time =	in_attribute->object_create_time;
	ca.object_modify_time = in_attribute->object_modify_time;
	ca.attr_modify_time = in_attribute->attr_modify_time;
	bcopy((char*)&ca,
        (char *)lin_attr->fs_specific, sizeof(_nasd_cheops_attribute_t));

	_nasd_cheops_enq_io(fio);  
	_nasd_cheops_enq_io(lio);  

	/* wait until all ios complete */ 
  NASD_LOCK_MUTEX(pio->flag_mutex);
  while ( pio->num_done < pio->num_ios ) {
    NASD_WAIT_COND(pio->ios_all_done, pio->flag_mutex);
  }
  NASD_UNLOCK_MUTEX(pio->flag_mutex);

   
	if(rc)
	  (cheops_Msg "setattr failed! \n");

	NASD_Free(fin_attr, sizeof(nasd_attribute_t));
	NASD_Free(lin_attr, sizeof(nasd_attribute_t));
	_nasd_cheops_io_freeall(pio);

}

/* XXX This routine is not used in NASD or Cheops, but it is called from
   siolib.  I thus must care about it.  SNL. */

void
_nasd_write_cl(
  nasd_cheops_handle_t   client_handle,
  nasd_cookie_t          in_cookie,
  nasd_identifier_t      in_identifier,
  nasd_offset_t          in_offset,
  nasd_len_t             in_len,
  nasd_partnum_t         in_partnum,
  nasd_len_t            *out_len,
  nasd_byte_t           *in_data,
  nasd_status_t         *nasd_status,
  nasd_rpc_status_t     *op_status)
{
	int i, rc=0;
	int mgr_id = 0;
	nasd_drive_handle_t h;
	nasd_asm_t *a;
	NASD_layout_t *lp;
	_nasd_cheops_cache_ent_t *e;
	nasd_cheops_bs_handle_t bs_handle;
	nasd_cheops_bs_handle_t *bs_handlep;
	_nasd_cheops_cache_key_t in_key;

	NASD_Malloc(a, sizeof(nasd_asm_t), (nasd_asm_t *));
	if (a==NULL) {
	  (cheops_Msg "CHEOPS error: malloc failed\n");
    *nasd_status = ENOMEM;
    *op_status = 0;
    return;
	}
	  
#if _NASD_CHEOPS_CL_DEBUG > 0
	(cheops_Msg
   "write_cl: issuing write to (obj=%" NASD_64u_FMT ", off=%"
   NASD_64u_FMT ", len=%d)\n", 
   in_identifier, in_offset, in_len);
#endif /* _NASD_CHEOPS_CL_DEBUG > 0 */

#if _NASD_CHEOPS_CL_RECORD_TIME > 0
	CHEOPS_DECLARE_TIME;
  CHEOPS_INIT_TIME;
  CHEOPS_BEGIN_TIME;
#endif /* _NASD_CHEOPS_CL_RECORD_TIME > 0 */

  /* lookup the stripe map of the byte-segment in the cache */
  in_key.object_id = 0;
	in_key.record_id = in_identifier;
#if 1
  rc = _nasd_cheops_cache_hold_ent(_nasd_cheops_bs_handle_cachep, in_key, &e);
	bs_handlep = (nasd_cheops_bs_handle_t *) e->data;
#else
	rc = _nasd_cheops_cache_read(_nasd_cheops_bs_handle_cachep, 
                               in_key, (caddr_t ) &bs_handle);
	bs_handlep = &bs_handle;
#endif

	if (rc) {
	  *nasd_status = EINVAL;
	  return;
	}

#if _NASD_CHEOPS_CL_DEBUG > 0
#endif /* _NASD_CHEOPS_CL_DEBUG > 0 */
  
	/* map logical the access to physical nasd objects */
	NASD_Malloc(lp, sizeof(NASD_layout_t), (NASD_layout_t *));
	if (lp==NULL) {
	  (cheops_Msg "malloc failed..\n");
	  *nasd_status = ENOMEM;
	  *op_status = 0;
	  return;
	}

  lp->numCol = bs_handlep->num_col;
  lp->stripeUnitSize = bs_handlep->stripe_unit_size;
  lp->numDataCol = bs_handlep->num_data_col;
  lp->dataStripeSize = lp->numDataCol*lp->stripeUnitSize;
  lp->layoutType = bs_handlep->raid_level;
  lp->map = _nasd_cheops_get_layout(lp->layoutType);
  for (i = 0; i < NASD_CHEOPS_MAX_STRIPE_SIZE; i++) {
    lp->identifier_list[i].nasd_identifier =
      bs_handlep->identifier_list[i].nasd_identifier;
    lp->identifier_list[i].disk_identifier =
      bs_handlep->identifier_list[i].disk_identifier;
	}
#if 1
	rc = _nasd_cheops_cache_release_ent(_nasd_cheops_bs_handle_cachep, e);
#endif
	  
#if _NASD_CHEOPS_CL_DEBUG > 0
	(cheops_Msg
   "write_cl: getting access map to (obj=%" NASD_64u_FMT ", off=%"
   NASD_64u_FMT ", len=%d)\n", 
   in_identifier, in_offset, in_len);
#endif /* _NASD_CHEOPS_CL_DEBUG > 0 */

	bzero((char *)a, sizeof(nasd_asm_t));
	_nasd_cheops_map_access(lp, in_offset, in_len, in_data, a);
	  
#if _NASD_CHEOPS_CL_DEBUG > 0
	(cheops_Msg
   "write_cl: invoking layout-specific write on (obj=%" NASD_64u_FMT ", off=%"
   NASD_64u_FMT ", len=%d)\n", 
   in_identifier, in_offset, in_len);
#endif /* _NASD_CHEOPS_CL_DEBUG > 0 */

	/* invoke the write operation */
	switch (lp->layoutType) {
	case RAID0:
	  /* fast path single drive objects  */
	  if (lp->numCol == 1)
	    rc = _nasd_cheops_noraid_write(a, out_len);
	  else
	    rc = _nasd_cheops_raid0_write(a, out_len);
	  break;
	case RAID1:
	  rc = _nasd_cheops_raid1_write(a, out_len);
	  break;
	case RAID5:
	  rc = _nasd_cheops_raid5_write(a, out_len);
	  break;
	default:
	  NASD_ASSERT(0);
	}

	/* update object length and modify time for cheops object */
	if (in_len != *out_len)
	  (cheops_Msg "CHEOPS Warning: write_cl: write to (obj=0x%" NASD_ID_FMT
     ", off=%" NASD_64u_FMT ") incomplete len=%d/%d)\n",
	   in_identifier, in_offset, in_len, *out_len);

	NASD_Free(lp, sizeof(NASD_layout_t));

	/* set return code */
	if (rc)
    *nasd_status = rc;
	else
    *nasd_status = 0;
	*op_status = 0; 

#if _NASD_CHEOPS_CL_RECORD_TIME > 0
	CHEOPS_END_TIME;
  CHEOPS_REPORT_TIME("_nasd_write_cl");
	CHEOPS_REPORT_BANDWIDTH((1.0*a.datalen/(1024*1024)));
#endif /* _NASD_CHEOPS_CL_RECORD_TIME > 0 */
}

void 
_nasd_read_cl(
  nasd_cheops_handle_t   client_handle,
  nasd_cookie_t          in_cookie,
  nasd_identifier_t      in_identifier,
  nasd_offset_t          in_offset,
  nasd_len_t             in_len,
  nasd_partnum_t         in_partnum,
  nasd_len_t            *out_len,
  nasd_byte_t           *out_data,
  nasd_status_t         *nasd_status,
  nasd_rpc_status_t     *op_status)
{
  int i, rc=0;
	int mgr_id = 0;
	nasd_drive_handle_t h;
	_nasd_cheops_cache_ent_t *e;
	nasd_cheops_bs_handle_t bs_handle;
	nasd_cheops_bs_handle_t *bs_handlep;
	nasd_asm_t *a;
	NASD_layout_t *lp;
	_nasd_cheops_cache_key_t in_key;

	NASD_Malloc(a, sizeof(nasd_asm_t), (nasd_asm_t *));
	if (a == NULL) {
	  (cheops_Msg "CHEOPS error: malloc failed\n");
    *nasd_status = ENOMEM;
    *op_status = 0;
    return;
	}

#if _NASD_CHEOPS_CL_DEBUG > 0
	(cheops_Msg "read_cl: issuing read to (obj=%" NASD_64u_FMT ", off=%"
   NASD_64u_FMT ", len=%d)...", in_identifier, in_offset, in_len);
#endif /* _NASD_CHEOPS_CL_DEBUG > 0 */

#if _NASD_CHEOPS_CL_RECORD_TIME > 0
	CHEOPS_DECLARE_TIME;
  CHEOPS_INIT_TIME;
  CHEOPS_BEGIN_TIME;
#endif /* _NASD_CHEOPS_CL_RECORD_TIME > 0 */

  /* lookup the stripe map of the byte-segment in the cache */
  in_key.object_id = 0;
	in_key.record_id = in_identifier;
#if 1 
  rc = _nasd_cheops_cache_hold_ent(_nasd_cheops_bs_handle_cachep, in_key, &e);
	bs_handlep = (nasd_cheops_bs_handle_t *)e->data;
#else
	rc = _nasd_cheops_cache_read(_nasd_cheops_bs_handle_cachep, 
                               in_key, (caddr_t ) &bs_handle);
	bs_handlep = &bs_handle;
#endif
	if (rc) {
	  *nasd_status = EINVAL;
	  return;
	}

	/* map logical the access to physical nasd objects */
	NASD_Malloc(lp, sizeof(NASD_layout_t), (NASD_layout_t *));
	if (lp==NULL) {
	  (cheops_Msg "malloc failed..\n");
    *nasd_status = ENOMEM;
    *op_status = 0;
	  return;
	}
  lp->numCol = bs_handlep->num_col;
  lp->stripeUnitSize = bs_handlep->stripe_unit_size;
  lp->numDataCol = bs_handlep->num_data_col;
  lp->dataStripeSize = lp->numDataCol*lp->stripeUnitSize;
  lp->layoutType = bs_handlep->raid_level;
  lp->map = _nasd_cheops_get_layout(lp->layoutType);
  for (i=0;i<NASD_CHEOPS_MAX_STRIPE_SIZE;i++) {
    lp->identifier_list[i].nasd_identifier =
      bs_handlep->identifier_list[i].nasd_identifier;
    lp->identifier_list[i].disk_identifier =
      bs_handlep->identifier_list[i].disk_identifier;
	}
#if 1
	rc = _nasd_cheops_cache_release_ent(_nasd_cheops_bs_handle_cachep, e);
#endif

	bzero((char *)a, sizeof(nasd_asm_t));
	_nasd_cheops_map_access(lp, in_offset, in_len, out_data, a);

	/* invoke the write operation */
	switch (lp->layoutType) {	  
	case RAID0:
	  if (lp->numCol == 1)
	    rc = _nasd_cheops_noraid_read(a, out_len);
	  else
	    rc = _nasd_cheops_raid0_read(a, out_len);
	  break;
	case RAID1:
	  rc = _nasd_cheops_raid1_read(a, out_len);
	  break;
	case RAID5:
	  rc = _nasd_cheops_raid5_read(a, out_len);
	  break;
	default:
	  NASD_ASSERT(0);
	}

	NASD_Free(lp, sizeof(NASD_layout_t));

	/* set return code */
	if (rc)
    *nasd_status = rc;
	else
    *nasd_status = 0;

	*op_status = 0;

#if _NASD_CHEOPS_CL_DEBUG > 0
	(cheops_Msg "read_cl: read %d\n...", *out_len);
#endif /* _NASD_CHEOPS_CL_DEBUG > 0 */

#if _NASD_CHEOPS_CL_RECORD_TIME > 0
	CHEOPS_END_TIME;
  CHEOPS_REPORT_TIME("read_cl");
	CHEOPS_REPORT_BANDWIDTH((1.0*a.datalen/(1024*1024)));
#endif /* _NASD_CHEOPS_CL_RECORD_TIME > 0 */
}

void 
_nasd_access_cl(
  nasd_cheops_handle_t   client_handle,
  nasd_cookie_t          in_cookie,
  nasd_identifier_t      in_identifier,
  nasd_cookie_t         *out_capability, /* FIX -- */
  nasd_status_t         *nasd_status,
  nasd_rpc_status_t     *op_status)
{
	int rc=0;
	int mgr_id = 0;
  nasd_cheops_bs_handle_t bs_handle;
  nasd_cheops_bs_lu_args_t args;
  nasd_cheops_bs_lu_res_t res;
	_nasd_cheops_cache_key_t in_key;

#if _NASD_CHEOPS_CL_DEBUG > 0
	(cheops_Msg "clerk: processing access to object %" NASD_64u_FMT "..\n",
   in_identifier);
#endif /* _NASD_CHEOPS_CL_DEBUG > 0 */
  /* lookup the stripe map of the byte-segment in the cache */
	in_key.object_id = 0;
	in_key.record_id = in_identifier;
#if 1
  rc = _nasd_cheops_cache_read(_nasd_cheops_bs_handle_cachep, 
                               in_key, (caddr_t) &bs_handle);
	if (rc) {
	  *nasd_status = EACCES;
	  return;
	}
#else
  h = _nasd_cheops_mgr_handles.hl[mgr_id].h;
  args.in_cookie = in_cookie;
  args.in_bsid = in_identifier;
  nasd_cheops_client_bs_lookup(h, &args, &res, op_status);
  rc = *nasd_status + *op_status;
  /*&bs_handle, nasd_status, op_status);*/
  if (rc) {
    (cheops_Msg "access_cl: lookup(%" NASD_64u_FMT ") returned rc=%d\n",
     in_identifier, rc);
  } else {
    bs_xxxx();
  }
#endif
	*nasd_status = NASD_SUCCESS;
  *op_status = 0; /* always successful */
}

void
_nasd_part_create_cl(
  nasd_cheops_handle_t   client_handle,
  nasd_cookie_t          in_cookie, 
  nasd_partnum_t         in_partnum,  
  nasd_uint64            in_blkcnt,
  nasd_status_t         *nasd_status,
  nasd_rpc_status_t     *op_status)
{
  *nasd_status = 0;
  *op_status = 0;
}

void 
_nasd_qos_create_cl(
  nasd_cheops_handle_t   client_handle,
  nasd_cookie_t          in_cookie,
  nasd_attribute_t       in_attribute,
  nasd_fieldmask_t       in_fieldmask,
  nasd_partnum_t         in_partnum,
  nasd_cheops_qos_req_t  in_qos_req,
  nasd_identifier_t     *out_identifier,
  nasd_attribute_t      *out_attribute,
  nasd_status_t         *nasd_status,
  nasd_rpc_status_t     *op_status)
{
  int mgr_id;
  nasd_cheops_handle_t h;
  nasd_cheops_bs_handle_t bs_handle;
  nasd_status_t  n_status;
  nasd_rpc_status_t o_status;
  nasd_cheops_bs_qc_args_t args;
  nasd_cheops_bs_qc_res_t res;

#if _NASD_CHEOPS_CL_DEBUG > 0
	(cheops_Msg "clerk: processing qos_create ...\n");
#endif /* _NASD_CHEOPS_CL_DEBUG > 0 */

  mgr_id = 0;
  h = _nasd_cheops_mgr_handles.hl[mgr_id].h;
  args.in_cookie = in_cookie;
  args.in_qos_req = in_qos_req;
  nasd_cheops_client_bs_qos_create(h, &args, &res, &o_status);
  *nasd_status = res.out_nasd_status;
  *out_identifier = res.out_bs_handle.bsid;
  *op_status = o_status;
}

void 
_nasd_create_cl(
  nasd_cheops_handle_t   client_handle,
  nasd_cookie_t          in_cookie,
  nasd_attribute_t       in_attribute,
  nasd_fieldmask_t       in_fieldmask,
  nasd_partnum_t         in_partnum,
  nasd_identifier_t     *out_identifier,
  nasd_attribute_t      *out_attribute,
  nasd_status_t         *nasd_status,
  nasd_rpc_status_t     *op_status)
{
  int mgr_id;
  nasd_cheops_handle_t h;
  nasd_cheops_bs_handle_t bs_handle;
  nasd_status_t  n_status;
  nasd_rpc_status_t o_status;
  nasd_cheops_bs_cr_args_t args;
  nasd_cheops_bs_cr_res_t res;

#if _NASD_CHEOPS_CL_DEBUG > 0
  (cheops_Msg "clerk: processing create ...");
#endif /* _NASD_CHEOPS_CL_DEBUG > 0 */

  mgr_id = 0;
  h = _nasd_cheops_mgr_handles.hl[mgr_id].h;
  args.in_cookie = in_cookie;
  nasd_cheops_client_bs_create(h, &args, &res, &o_status);
#if _NASD_CHEOPS_CL_DEBUG > 0
  (cheops_Msg "create: id=%" NASD_64u_FMT ", group size=%d..\n",
   bs_handle.bsid, bs_handle.num_data_col);
#endif /* _NASD_CHEOPS_CL_DEBUG > 0 */
  *nasd_status = res.out_nasd_status;
  *out_identifier = res.out_bs_handle.bsid;
  *op_status = o_status;

#if _NASD_CHEOPS_CL_DEBUG > 0
  (cheops_Msg "id = %" NASD_64u_FMT "...\n", *out_identifier);
#endif /* _NASD_CHEOPS_CL_DEBUG > 0 */
}

void 
_nasd_remove_cl(
  nasd_cheops_handle_t   client_handle,
  nasd_cookie_t          in_cookie,
  nasd_identifier_t      in_identifier,
  nasd_status_t         *nasd_status,
  nasd_rpc_status_t     *op_status)
{
  int mgr_id;
  nasd_cheops_handle_t h;
  nasd_cheops_bs_handle_t bs_handle;
  nasd_status_t  n_status;
  nasd_rpc_status_t o_status;
  nasd_cheops_bs_rm_args_t args;
  nasd_cheops_bs_rm_res_t res;

  (cheops_Msg
   "clerk: processing remove of object 0x%" NASD_ID_FMT " ...\n",
   in_identifier);

  mgr_id = 0;
  h = _nasd_cheops_mgr_handles.hl[mgr_id].h;
  args.in_cookie = in_cookie;
  args.in_bsid = in_identifier;
  nasd_cheops_client_bs_remove(h, &args, &res, &o_status);
  n_status = *nasd_status = res.out_nasd_status;
  *op_status = o_status;
  if (n_status != NASD_SUCCESS)
    (cheops_Msg "remove_cl failed rc=%d\n", n_status);
  *op_status = 0; /* always successful */
}

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