/*
 * Copyright Red Hat
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA. 
 *
 * $Id: //eng/uds-releases/jasper/src/uds/volumeStore.c#2 $
 */

#include "geometry.h"
#include "indexLayout.h"
#include "logger.h"
#include "uds-error.h"
#include "volumeStore.h"


/*****************************************************************************/
void closeVolumeStore(struct volume_store *volumeStore)
{
#ifdef __KERNEL__
  if (volumeStore->vs_client != NULL) {
    dm_bufio_client_destroy(volumeStore->vs_client);
    volumeStore->vs_client = NULL;
  }
#else
  if (volumeStore->vs_region != NULL) {
    putIORegion(volumeStore->vs_region);
    volumeStore->vs_region = NULL;
  }
#endif
}

/*****************************************************************************/
void destroyVolumePage(struct volume_page *volumePage)
{
#ifdef __KERNEL__
  releaseVolumePage(volumePage);
#else
  FREE(volumePage->vp_data);
  volumePage->vp_data = NULL;
#endif
}

/*****************************************************************************/
int initializeVolumePage(const struct geometry *geometry,
                         struct volume_page    *volumePage)
{
#ifdef __KERNEL__
  volumePage->vp_buffer = NULL;
  return UDS_SUCCESS;
#else
  return ALLOCATE_IO_ALIGNED(geometry->bytesPerPage, byte, __func__,
                             &volumePage->vp_data);
#endif
}

/*****************************************************************************/
int openVolumeStore(struct volume_store *volumeStore,
                    IndexLayout  *layout,
                    unsigned int  reservedBuffers __attribute__((unused)),
                    size_t        bytesPerPage)
{
#ifdef __KERNEL__
  return openVolumeBufio(layout, bytesPerPage, reservedBuffers,
                         &volumeStore->vs_client);
#else
  volumeStore->vs_bytesPerPage = bytesPerPage;
  return openVolumeRegion(layout, &volumeStore->vs_region);
#endif
}

/*****************************************************************************/
void prefetchVolumePages(const struct volume_store *vs __attribute__((unused)),
                         unsigned int physicalPage __attribute__((unused)),
                         unsigned int pageCount __attribute__((unused)))
{
#ifdef __KERNEL__
  dm_bufio_prefetch(vs->vs_client, physicalPage, pageCount);
#else
  // Nothing to do in user mode
#endif
}

/*****************************************************************************/
int prepareToWriteVolumePage(const struct volume_store *volumeStore
                             __attribute__((unused)),
                             unsigned int         physicalPage
                             __attribute__((unused)),
                             struct volume_page  *volumePage
                             __attribute__((unused)))
{
#ifdef __KERNEL__
  releaseVolumePage(volumePage);
  struct dm_buffer *buffer = NULL;
  byte *data = dm_bufio_new(volumeStore->vs_client, physicalPage, &buffer);
  if (IS_ERR(data)) {
    return -PTR_ERR(data);
  }
  volumePage->vp_buffer = buffer;
#else
  // Nothing to do in user mode
#endif
  return UDS_SUCCESS;
}

/*****************************************************************************/
int readVolumePage(const struct volume_store *volumeStore,
                   unsigned int               physicalPage,
                   struct volume_page        *volumePage)
{
#ifdef __KERNEL__
  releaseVolumePage(volumePage);
  byte *data = dm_bufio_read(volumeStore->vs_client, physicalPage,
                             &volumePage->vp_buffer);
  if (IS_ERR(data)) {
    return logWarningWithStringError(-PTR_ERR(data),
                                     "error reading physical page %u",
                                     physicalPage);
  }
#else
  off_t offset = (off_t) physicalPage * volumeStore->vs_bytesPerPage;
  int result = readFromRegion(volumeStore->vs_region, offset,
                              getPageData(volumePage),
                              volumeStore->vs_bytesPerPage, NULL);
  if (result != UDS_SUCCESS) {
    return logWarningWithStringError(result,
                                     "error reading physical page %u",
                                     physicalPage);
  }
#endif
  return UDS_SUCCESS;
}

/*****************************************************************************/
void releaseVolumePage(struct volume_page *volumePage __attribute__((unused)))
{
#ifdef __KERNEL__
  if (volumePage->vp_buffer != NULL) {
    dm_bufio_release(volumePage->vp_buffer);
    volumePage->vp_buffer = NULL;
  }
#else
  // Nothing to do in user mode
#endif
}

/*****************************************************************************/
void swapVolumePages(struct volume_page *volumePage1,
                     struct volume_page *volumePage2)
{
  struct volume_page temp = *volumePage1;
  *volumePage1 = *volumePage2;
  *volumePage2 = temp;
}

/*****************************************************************************/
int syncVolumeStore(const struct volume_store *volumeStore)
{
#ifdef __KERNEL__
  int result = -dm_bufio_write_dirty_buffers(volumeStore->vs_client);
#else
  int result = syncRegionContents(volumeStore->vs_region);
#endif
  if (result != UDS_SUCCESS) {
    return logErrorWithStringError(result, "cannot sync chapter to volume");
  }
  return UDS_SUCCESS;
}

/*****************************************************************************/
int writeVolumePage(const struct volume_store *volumeStore,
                    unsigned int               physicalPage,
                    struct volume_page        *volumePage)
{
#ifdef __KERNEL__
  dm_bufio_mark_buffer_dirty(volumePage->vp_buffer);
  return UDS_SUCCESS;
#else
  off_t offset = (off_t) physicalPage * volumeStore->vs_bytesPerPage;
  return writeToRegion(volumeStore->vs_region, offset, getPageData(volumePage),
                       volumeStore->vs_bytesPerPage,
                       volumeStore->vs_bytesPerPage);
#endif
}
