/*
 * Common interface for the level drivers
 *
 * (C) 2000 by Jakob Oestergaard
 *
 * This source is covered by the GNU GPL, the same as all Linux kernel
 * sources.
 */

#ifndef __RRC_COMMON_H
#define __RRC_COMMON_H

#include "raidreconf.h"
#include <limits.h>

typedef struct wish_t {
	int source_disk_id;
	unsigned long source_rblock;
	int sink_disk_id;
	unsigned long sink_rblock;
	struct wish_t *next;
} wish_t;

typedef struct fulfilled_t {
	int sink_disk_id;
	unsigned long sink_rblock;
	char *data;
	struct fulfilled_t *next;
} fulfilled_t;

/*
 * About free blocks:   A block is considered free, if it has been
 * read from the source disk.  Free means ``sink is free to write stuff
 * here''.
 * Thus, we only account free blocks on the source disks, sink-only disks
 * are per definition always entirely free.  nr_free_blocks() return only
 * number of free blocks on source disks.
 *
 * When the sink wishes for a block, this means it wants the data in
 * that global block read from somewhere on the old array.  It also
 * means, that it wants the sink-disk,sink-disk-block freed so that it
 * can safely write to it.  Freeing the block will mean reading it to
 * memory.  So in real-life (TM), wishing for one block can often mean
 * freeing two blocks, where only one can actually be written to
 * somewhere.  The freed blocks will end up in the list of fulfilled
 * requests, so the list of fulfilled requests _will_ actually hold
 * (lots of) blocks that hasn't directly been asked for by the sink
 * driver.
 * If the sink-disk,sink-disk-block locations of the un-wished-for
 * blocks are free themselves, we can easily flush these blocks.  If
 * they are not, we should probably look for a way to free them.
 * This, in turn, may leed to freeing even more un-wished-for blocks.
 * The sink will never get more than two blocks from each wish it
 * files, but this is still bad if we recurse for long (quadratic
 * memory requirements).
 * There is no simple way (that I thought of at least) to ensure that
 * we follow a request/read/write pattern so that this program can work
 * correctly at all times within some limited memory space (memory much
 * less than size of array).  Therefore we always run an algorithm check
 * before we actually perform the reconfiguration.  The algorithm check
 * is a full run of the reconfiguration, only with physical disk access
 * turned off. If such a run can succeed, so can one with physical disk
 * access.
 *
 * If algorithm check fails, we probably could try a multiple-pass
 * strategy. For example, converting chunk sizes first, then levels,
 * or going thru intermediate levels...  Eventually we could use a
 * scratch disk,  or use the parity blocks on raid-[145] as temporary
 * buffers.
 *
 * Driver vs. common-layer operation:   
 *
 * request:
 *  The sink driver will request that a number of blocks are read from
 *  the old array, so that it can write them to their proper location
 *  on the new array.  Wishes are inserted with global block
 *  addresses.  Global block N on the sink drive is usually not the
 *  same as global block N on the source drive, so before the wish is
 *  hooked into the wish-list, the common layer will have the source
 *  driver map the global block number into a
 *  source-disk,source-disk-block address.
 *
 * read:
 *  The source driver will use the common layer to fulfill requests
 *  that look good, that is, request sequences that are large and
 *  sequential.  As blocks are read, the common layer will mark them
 *  as free.
 *
 * write:
 *  The common layer will flush the fulfilled requests to the sink
 *  disks.  As the source driver will usually ask for sink-disk
 *  sequential regions, the writer can assume that just flushing the
 *  sorted sequences of fulfilled requests will perform reasonably
 *  well.
 * */

const char *setup_free_blocks (unsigned ndisks, rrc_disk_t * diskcfg);
const char *initialize_unique_disks (void);

/* 
 * Drivers should not touch these directly
 */
extern unsigned max_wishes;
extern unsigned long reconf_block_size;

extern char **source_disk_free_map;
extern unsigned long *source_disk_free_size;

extern unsigned long source_blocks, sink_blocks;

/*
 * Drivers should use these routines instead...
 */

/*
 * These are for the sink requester 
 */

int can_wish_again (void);
int is_gblock_in_source (unsigned long gblock);
void insert_wish (unsigned long gblock);

/*
 * These are generic
 */
unsigned long nr_free_blocks (void);
int is_diskid_in_source (int diskid);

int is_disk_block_free (int diskid, unsigned long block);

wish_t *wish_list_source_diskid (int source_disk_id);
fulfilled_t *gift_list_diskid (int disk_id);

const char *generic_write_blocks (int partial);

void debug_print_nonfree_blocks (void);
void debug_print_wish_list (void);

void print_common_stats (void);

/*
 * These are for the common reader stuff
 */
void fulfill_wishes (int partial);
int must_fulfill_more (int partial);
unsigned nr_wishes_left (void);
unsigned nr_gifts_left (void);
void fulfill_wish (int diskid, unsigned long begin, unsigned long end);	/* do the reading */

/*
 * This one's for the initial block freeing when we shrink arrays 
 */
void mark_disk_block_free (int diskid, unsigned long block);

/* to be used in free_block_above_gblock() only! */
void unchecked_mark_disk_block_free (int diskid, unsigned long block);

void mark_disk_block_unfree (int diskid, unsigned long block);


#endif
