/*
 * nasd_setup_disk.c
 *
 * Compute disk layout. Broken into separate file
 * for compilation into a drive-utils-support
 * library.
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 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 <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_sys.h>
#include <nasd/nasd_layout.h>
#include <nasd/nasd_trace.h>
#include <nasd/nasd_trace_dr.h>
#include <nasd/nasd_timeout.h>
#include <nasd/nasd_drive_utils.h>

/*
 * As passed in, num_real_sectors is the number of 512-byte
 * sectors, not the number of NASD_OD_SECT_SIZE sectors.
 * Ideally, NASD_OD_SECT_SIZE is 512, but in LINUX that
 * just isn't possible. Yippee. So we have to map our
 * expectations.
 */
nasd_status_t
nasd_drive_compute_basic_layout(
  unsigned long               num_real_sectors,
  nasd_drive_layout_basic_t  *layoutp)
{
  int locap, hicap, lastcap, isover, kick_sects, ko, sect_align;
  long refblocks, firstoff, sum;
  nasd_blkcnt_t capacity;

  NASD_ASSERT((1<<NASD_OD_SECT_SHIFT) == NASD_OD_SECT_SIZE);
  NASD_ASSERT((1<<NASD_OD_BLOCK_SHIFT) == NASD_OD_BASIC_BLOCKSIZE);

  NASD_ASSERT((NASD_OD_SECTORS_PER_BLK * NASD_OD_SECT_SIZE)
    == NASD_OD_BASIC_BLOCKSIZE);

  NASD_ASSERT(sizeof(nasd_od_disk_t) == NASD_OD_SECT_SIZE);
  NASD_ASSERT(sizeof(nasd_od_node_t) == NASD_OD_BASIC_BLOCKSIZE);
  NASD_ASSERT(sizeof(nasd_od_direct_ptr_t) == NASD_OD_PTR0_SIZE);
  NASD_ASSERT(sizeof(nasd_od_indirect_ptr_t) == NASD_OD_PTR_SIZE);

  sect_align = nasd_od_force_align_disk_boundary >> NASD_OD_SECT_SHIFT;

  if (num_real_sectors > nasd_uint64cast(0xffffffffff)) {
    return(NASD_FAIL);
  }

#if (NASD_OD_SECT_SHIFT < 9)
    layoutp->num_sectors = num_real_sectors << (9 - NASD_OD_SECT_SHIFT);
#elif (NASD_OD_SECT_SHIFT == 9)
    layoutp->num_sectors = num_real_sectors;
#elif (NASD_OD_SECT_SHIFT > 9)
    layoutp->num_sectors = num_real_sectors >> (NASD_OD_SECT_SHIFT - 9);
#else /* NASD_OD_SECT_SHIFT CHECKING */
    /*
     * Okay, it's not <9, it's not =9, and it's not >9.
     * What is it, a grape?
     */
    NASD_PANIC();
#endif /* NASD_OD_SECT_SHIFT CHECKING */

  layoutp->firstref_offset =
    ((nasd_overhead_sects + sect_align - 1)
    / sect_align)
    * sect_align;
  layoutp->header1_blk = layoutp->firstref_offset - 1;

  /*
   * This code is so lame I should be ashamed, but I'm kinda
   * tired and don't remember how to do algebra. Someone should
   * fix this later. Basically, it does a binary search for the
   * "right" answer.
   */
  lastcap = 0;
  locap = 1;
  capacity = (layoutp->num_sectors/NASD_OD_SECTORS_PER_BLK) / 2;
  hicap = (layoutp->num_sectors/NASD_OD_SECTORS_PER_BLK);
  isover = 1;
  while (isover || ((lastcap != (capacity + 1)) && (lastcap != capacity)))
  {
    if (isover && ((capacity-1) == lastcap))
      capacity = lastcap;
    lastcap = capacity;
    refblocks = (capacity + (NASD_OD_REFS_PER_BLOCK-1))
      / NASD_OD_REFS_PER_BLOCK;
    firstoff = layoutp->firstref_offset + (refblocks * NASD_OD_SECTORS_PER_BLK);
    /* 1 below for trailing header copy */
    sum = (capacity*NASD_OD_SECTORS_PER_BLK) + firstoff + 1;
    if (sum >= layoutp->num_sectors) {
      hicap = capacity;
      capacity = ((capacity - locap)/2) + locap;
      isover = 1;
    }
    else {
      locap = capacity;
      capacity = ((hicap - capacity)/2) + capacity;
      isover = 0;
    }
  }

  /* number of refcnt blocks */
  layoutp->num_refblocks = refblocks;

  layoutp->firstblock_offset = firstoff;

  layoutp->num_blocks = capacity;

  if (sect_align) {
    ko = layoutp->firstblock_offset % sect_align;
    if (ko) {
      kick_sects = sect_align - ko;
      layoutp->firstblock_offset += kick_sects;
      layoutp->num_blocks -= (kick_sects + (NASD_OD_SECTORS_PER_BLK-1))
        / NASD_OD_SECTORS_PER_BLK;
    }
  }

  layoutp->header2_blk = layoutp->firstblock_offset
    + (layoutp->num_blocks * NASD_OD_SECTORS_PER_BLK);

  layoutp->last_data_sector = (layoutp->num_blocks * NASD_OD_SECTORS_PER_BLK)
    + layoutp->firstblock_offset - 1;

  return(NASD_SUCCESS);
}

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