#include <system.h>
#include <nand.h>
#include <chunks.h>
#include <blocks.h>
#include <tm.h>

#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <string.h>

/******************************************/
/* 				global structs									*/
/******************************************/
extern int maintid;
extern int tids[NAND_CHIP_COUNT];

static char mode[10] = "a+";

extern uint32_t program[NAND_PAGE_COUNT];

extern tm_t tm;

extern uint32_t real_writes;
extern uint32_t real_reads;
extern uint32_t reclaim_writes;

static FILE* open_file(char* fname){
	return fopen(fname, mode);
}

static void close_file(FILE* file){
	fclose(file);
}

static char filename[10] = "tm.c";

#ifdef PROFILING
extern uint32_t real_writes;
extern uint32_t real_reads;
extern uint32_t reclaim_reads;
extern uint32_t reclaim_writes;
extern uint32_t chip_writes[NAND_CHIP_COUNT];
#endif
/****************************************************************************/
/* 												Translation Manager								  							*/
/****************************************************************************/
/****************************************************
									 utils
 ****************************************************/
uint32_t
tm_get_blk_idx_by_page(uint32_t page_in_blk){
	LOG("starting. page_in_blk %u\n", page_in_blk);
	page_in_blk &= NAND_EU_MASK;
	LOG("first page in block %u\n", page_in_blk);

	uint32_t i;
	for(i=0; i< TM_BLOCK_COUNT;i++){
		if(tm.block[i].address == page_in_blk){
			return i;
		}
	}

	return IDX_EMPTY;
}

bool_t
tm_bitmap_get(uint32_t page){//LOG("page %u bit %u\n", page, (tm.bitmap[page / 8] >> (page % 8)) & 0x01);
	return (tm.bitmap[page / 8] >> (page % 8)) & 0x01;
}

void
tm_bitmap_set(uint32_t page, uint8_t bit){
	uint32_t blk_idx = tm_get_blk_idx_by_page(page);LOG("starting. blk_idx %u page %u bit %u\n", blk_idx, page, bit);

	if(bit){
		assert(!tm_bitmap_get(page));
		tm.bitmap[page / 8] |= (1 << (page % 8));
		tm.block[blk_idx].obs_count--;
	}
	else{
		assert(tm_bitmap_get(page));
		tm.bitmap[page / 8] &= (~(1 << (page % 8)));
		tm.block[blk_idx].obs_count++;
	}
//	LOG("block %u obs_count is now %u\n", blk_idx, tm.block[blk_idx].obs_count);
}

uint32_t
tm_calc_chip_start_addr(uint32_t chip_idx){LOG("starting. chip_idx %u\n", chip_idx);
	return chip_idx * NAND_PAGES_PER_CHIP + CH_BLOCKS_PER_CHIP * NAND_PAGES_PER_ERASE_UNIT;
}

uint32_t
tm_calc_chip_end_addr(uint32_t chip_idx){LOG("starting. chip_idx %u\n", chip_idx);
	return tm_calc_chip_start_addr(chip_idx+1);
}

bool_t
tm_is_reclaim_req(tm_specific_t* specific){
	return (specific->rec_chip_idx != IDX_EMPTY);
}

bool_t
tm_is_read_req(req_t* tm_req){
	uint32_t cmd = TM_REQ_GET_SPECIFIC(tm_req)->cdb->cmd;LOG("starting. cmd %s\n", (cmd==SCSI_READ)?"READ":"WRITE");
	return (SCSI_READ == cmd);
}

bool_t
tm_is_write_req(req_t* req){
	return (SCSI_WRITE == TM_REQ_GET_SPECIFIC(req)->cdb->cmd);
}

bool_t
tm_is_simple_write_req(req_t* req){
	return (tm_is_write_req(req)
			    &&
			    !tm_is_reclaim_req(TM_REQ_GET_SPECIFIC(req)));
}

bool_t
tm_is_new_req(req_t* tm_req){
	tm_specific_t* specific= TM_REQ_GET_SPECIFIC(tm_req);
	return (specific->lba_prev == 0 && specific->lba_next == 0);
}

bool_t
tm_is_with_pending_reclaim(req_t* req){
	return (TM_REQ_GET_SPECIFIC(req)->pending_reclaim_req != 0);
}

bool_t
tm_is_reclaim_or_with_pending(req_t* req){LOG("req %x\n", req);
	return (tm_is_reclaim_req(req->specific) || tm_is_with_pending_reclaim(req));
}

///**
// *
// * Assumptions:
// * 1. last_known_written_page from old write req - if not is_page_read, req not processed so this does not constitute as identical
// * 2. pge is from new rec req - never empty
// */
//bool_t
//tm_is_identical_pages(tm_specific_t* specific, uint32_t page){
//	uint32_t last_known_written_page = specific->last_known_written_page;
//	return (last_known_written_page || !(specific->is_page_read));
//}
/*****************************************************/
/* 					    Globals 				 										 */
/*****************************************************/

/************************************************
							handled lba list
************************************************/
list_t*
list_alloc_node(uint32_t lba, req_t* req){
	list_t* new_node = (list_t*)malloc(sizeof(list_t));
	LOG("starting\n");
	new_node->next = 0;
	new_node->prev = 0;
	new_node->lba  = lba;
	new_node->req  = req;

	return new_node;
}

list_t*
list_lookup(list_t** list, uint32_t lba){
	list_t* node = *list;
//	LOG("starting lba %u (list %x)\n", lba, *list);

	/* if no lba*/
	if(lba == IDX_EMPTY){LOG("no lba!\n");
		return 0;
	}

	/* if no list*/
	if(node == 0){LOG("list is empty\n");
		return 0;
	}

//	{
//		list_t* nd = *list;
//		int i =0;
//		LOG("@@@ display all list memebers @@@\n");
//		while(nd != 0){
//			LOG("nd %x nd->req %x nd->req->specific %x\n", nd, nd->req, nd->req->specific);
////			LOG("%d) nd->lba %u %s, magic_num %u\n", i,nd->lba, (((tm_specific_t*)(nd->req->specific))->cdb->cmd==SCSI_READ)?"READ":"WRITE", ((tm_specific_t*)(nd->req->specific))->cdb->magic_num);
//			assert(nd->lba < TM_SECTOR_COUNT);
//			nd = nd->next;
//			i++;
//		}
//	}

//	LOG(" looking for %u\n", lba);
	while(node != 0){
#ifdef Fanatic
		//		LOG(" node %x\n", node);
//				LOG(" node->lba %u, node->req %x,  (%s, magic_num %u)\n",
		//				node->lba, node->req, (((tm_specific_t*)(node->req->specific))->cdb->cmd==SCSI_WRITE)?"WRITE":"READ",
		//				((tm_specific_t*)(node->req->specific))->cdb->magic_num);
		assert(node->lba < TM_SECTOR_COUNT);
		//		LOG(" node->lba %u, lba %u\n", node->lba, lba);
#endif

		if(node->lba != lba){
//			LOG("node->lba %u lba %u\n", node->lba, lba);
			node = node->next;
			continue;
		}
		//LOG(" node->lba %u, lba %u\n", node->lba, lba);

//		LOG("returning node with lba %u (%s, magic_num %u)\n", node->lba, (((tm_specific_t*)(node->req->specific))->cdb->cmd==SCSI_WRITE)?"WRITE":"READ",
//				((tm_specific_t*)(node->req->specific))->cdb->magic_num);
		return node;
	}

//	LOG("havent found lba %u, returning null\n", lba);
	return 0;
}

void
list_push(list_t** list, uint32_t lba, req_t* req){
	LOG("starting. lba %u, req %x req lba %u magic_num %u\n", lba, req, ((tm_specific_t*)(req->specific))->cdb->start_lba, ((tm_specific_t*)(req->specific))->cdb->magic_num);
	assert(((tm_specific_t*)(req->specific))->cdb->start_lba == lba);
	list_t* new_node = list_alloc_node(lba, req);
#ifdef Fanatic
	{
		list_t* node = *list;
		while(node != 0){
			LOG("node->lba %u node->req %x req %x\n", node->lba, node->req, req);
//			LOG("node->req - lba %u %s magic_num %u. req  - lba %u %s magic_num %u\n",
//					node->lba, ((tm_specific_t*)(node->req->specific))->cdb->cmd==SCSI_READ?"READ":"WRITE", ((tm_specific_t*)(node->req->specific))->cdb->magic_num,
//					((tm_specific_t*)(req->specific))->cdb->start_lba, ((tm_specific_t*)(req->specific))->cdb->cmd==SCSI_READ?"READ":"WRITE", ((tm_specific_t*)(req->specific))->cdb->magic_num);
			if(node->req == req || node->lba == lba){
				LOG("node->lba %u node->req %x req %x\n", node->lba, node->req, req);
				printf("list_push()  -node->lba %u node->req %x req %x\n", node->lba, node->req, req);
				assert(node->req != req);
				assert(node->lba != lba);
			}
			node = node->next;
		}
	}
#endif
	if(*list == 0){
		*list = new_node;
		return;
	}

	new_node->next = *list;
	new_node->next->prev = new_node;
	*list = new_node;

  return;
}

void
req_lba_list_pop(req_t* req);

/**
 *
 * return 0 if popped, 1 if not found
 */
int
list_pop_specific(list_t** list, uint32_t lba, req_t* req){
	list_t* node;
	LOG("starting lba %u\n", lba);
	assert(lba < TM_SECTOR_COUNT);

	node = list_lookup(list, lba);
#ifdef Fanatic
	assert(node->req ==req);
#endif
	if(node !=0 && node->req == req){LOG("node %x node->lba %u\n", node, node->lba);
		/* remove from lba req list*///		LOG("pop req lba %u %s with from req lba list\n", lba, ((tm_specific_t*)(node->req->specific))->cdb->cmd == SCSI_WRITE?"WRITE":"READ");
		req_lba_list_pop(node->req);

		/* if this is the first node*/
		if((*list)->lba == lba){
			*list =	node->next;
		}

		/* if has prev*/
		if(node->prev != 0){
			node->prev->next = node->next;
		}

		/* if has next*/
		if(node->next != 0){
			node->next->prev = node->prev;
		}

#ifdef Fanatic
		/*verify req and node are no longer pointed by list*/
		list_t *p = *list;
		while(p){
			assert(p != node);
			assert(p->lba != node->lba);
			assert(p->req != node->req);
			p = p->next;
		}
#endif

		free(node);LOG("return 0\n");

		return 0;
	}LOG("ERROR\n");

  return -1;
}

/**
 *
 * return 0 if popped, 1 if not found
 */
int
list_pop(list_t** list, uint32_t lba){
	list_t* node;
	LOG("starting lba %u\n", lba);
	assert(lba < TM_SECTOR_COUNT);
	node = list_lookup(list, lba);
	if(node !=0){
//		LOG("node %x\n", node);
		LOG("node->lba %u\n", node->lba);
		/* remove from lba req list*/
//		LOG("pop req lba %u %s with from req lba list\n", lba, ((tm_specific_t*)(node->req->specific))->cdb->cmd == SCSI_WRITE?"WRITE":"READ");
		req_lba_list_pop(node->req);

		/* if this is the first node*/
		if((*list)->lba == lba){
			*list =	node->next;
			if((*list))	(*list)->prev = 0;
		}

		/* if has prev*/
		if(node->prev != 0){
			node->prev->next = node->next;
		}

		/* if has next*/
		if(node->next != 0){
			node->next->prev = node->prev;
		}

		free(node);

		LOG("return 0\n");
		return 0;
	}

	LOG("ERROR\n");
  return -1;
}


/************************************************/
/*							general list  							*/
/************************************************/
list_t*
tm_list_alloc_node(req_t* req){
	list_t* new_node = (list_t*)malloc(sizeof(list_t));LOG("starting\n");
	new_node->next = 0;
	new_node->prev = 0;
	new_node->req  = req;
	new_node->lba  = TM_REQ_GET_SPECIFIC(req)->curr_lba;

	return new_node;
}

list_t*
tm_list_lookup(list_t** list, req_t* req){
	list_t* node = *list;LOG("starting looking for %x \n", req);assert(req);

	/* if no list*/
	if(node == 0){
		return 0;
	}

	while(node != 0){
		if(node->req != req){
			node = node->next;
			continue;
		}

		LOG("found relevant_node %x\n", node);
		return node;
	}

	LOG("none found\n");
	return 0;
}

/**
 * push to end of list
 *
 */
void
tm_list_push(list_t** list, req_t* req){LOG("starting. *list %x req %x\n", *list, req);
	list_t* new_node = tm_list_alloc_node(req);

//	{
//		list_t* node = *list;
//		tm_specific_t* specific = TM_REQ_GET_SPECIFIC(req);
//		LOG("push lba %u %s magic_num %u \n", specific->cdb->start_lba, specific->cdb->cmd==SCSI_READ?"READ":"WRITE", specific->cdb->magic_num);
//		while(node != 0){
//			specific = TM_REQ_GET_SPECIFIC(node->req);LOG("node->req - lba %u %s magic_num %u \n",node->lba, specific->cdb->cmd==SCSI_READ?"READ":"WRITE", specific->cdb->magic_num);
//			assert(node->req != req);
//			node = node->next;
//		}
//	}
	if(*list == 0){
		*list = new_node;LOG("list empty, push as head\n");
		return;
	}

	list_t* node = *list; LOG("list not empty, push to tail\n");

	while(node->next != 0){//LOG("node->next %x\n", node->next);	assert(node->req != req);
		node = node->next;
	}//LOG("node->next %x\n", node->next);

	node->next = new_node;
	new_node->prev = node;//LOG("return\n");

  return;
}

/**
 *
 * return 0 if popped, -1 if not found
 */
req_t*
tm_list_pop_specific(list_t** list, list_t* node){
	/* if this is the first node*/
	if((*list) == node){ assert(node->prev == 0);
		*list =	node->next;LOG("pop head\n");
		if((*list))	(*list)->prev = 0;
	}

	/* if has prev*/
	if(node->prev != 0){
		node->prev->next = node->next;
	}

	/* if has next*/
	if(node->next != 0){
		node->next->prev = node->prev;
	}

	req_t* req = node->req;
	free(node);

	LOG("success. return req %x\n", req);return req;
	return req;
}

/**
 *
 * return 0 if popped, -1 if not found
 */
req_t*
tm_list_pop(list_t** list, req_t* req){
	list_t* node;	LOG("starting req %x\n", req);assert(req);
	node = tm_list_lookup(list, req);
	if(node !=0){
		return tm_list_pop_specific(list, node);
	}

	LOG("ERROR\n");assert(0);return 0;
}

/**
 *
 * return req if popped, 0 if none found
 */
req_t*
tm_list_pop_head(list_t** list){
	req_t* req;LOG("starting\n");
	if((*list) == 0){
		return 0;
	}

	return tm_list_pop(list, (*list)->req);
}

/****************************************************
									 req lba list
 ****************************************************/
/* lba req list
 *
 * push to the end of the list*/
void
req_lba_list_push(req_t* p, req_t* req){
	tm_specific_t* specific;
	uint32_t orig_lba = TM_REQ_GET_SPECIFIC(p)->curr_lba;
	LOG("starting p %x req %x\n", p, req);
//	{
//		/*print all list */
//		req_t* r = p;
//
//		specific = (tm_specific_t*)(r->specific);
//		while(specific->lba_prev){
//			r = specific->lba_prev;
//			specific = (tm_specific_t*)(r->specific);
//		}
//
//		int i = 0;
//		LOG("print entire list\n");
//		while(r != 0){
//			specific = (tm_specific_t*)(r->specific);
////			LOG("@%d - %s magic_num %u\n", i, (((tm_specific_t*)(r->specific))->cdb->cmd==SCSI_WRITE)?"WRITE":"READ",
////					((tm_specific_t*)(r->specific))->cdb->magic_num);
//			r = specific->lba_next;
//			i++;
//		}
//	}

	/* put this req in end of this lba requests list*/
	specific = TM_REQ_GET_SPECIFIC(p);
	int i=0;
	while(specific->lba_next != 0){
		LOG("now p %x\n", p);
//		{
//			/*assert this list indeed contains only this lba*/
//			if(orig_lba != specific->curr_lba){
//				LOG("%d) orig_lba %u specific->curr_lba %u\n", i ,orig_lba, specific->curr_lba);
//				assert(orig_lba == specific->curr_lba);
//			}
//			i++;
//		}

		p = specific->lba_next;
		assert(p!=req); /* assert no double push*/
		specific = TM_REQ_GET_SPECIFIC(p);
	}

	LOG("modify relevant next and prev \n");
	specific->lba_next = req;
	TM_REQ_GET_SPECIFIC(req)->lba_prev = p;

//	tm_list_push(&(tm.resource_pending), req);
	TM_REQ_GET_SPECIFIC(req)->enq_reason = TM_ENQUEUE_REASON_LBA_TAKEN;LOG("req %x enq_reason TM_ENQUEUE_REASON_LBA_TAKEN \n", req);
	return;
}

void
req_lba_list_pop(req_t* req){
	tm_specific_t* specific = TM_REQ_GET_SPECIFIC(req);
	LOG("starting\n");
	if(specific->lba_next !=0){
		LOG("next which is req %x lba %u %s now has null prev\n",
				specific->lba_next, TM_REQ_GET_SPECIFIC(specific->lba_next)->curr_lba, ((tm_specific_t*)(specific->lba_next->specific))->cdb->cmd == SCSI_WRITE?"WRITE":"READ");
		TM_REQ_GET_SPECIFIC(specific->lba_next)->lba_prev = 0;
		specific->lba_next = 0;
	}
}

void
req_lba_list_pop_specific(req_t* req){
	tm_specific_t* specific = TM_REQ_GET_SPECIFIC(req);
	LOG("starting\n");

	req_t* prev = specific->lba_prev;
	req_t* next = specific->lba_next;

	if(next !=0){
		TM_REQ_GET_SPECIFIC(next)->lba_prev = prev;
	}
	if(prev != 0){
		TM_REQ_GET_SPECIFIC(prev)->lba_next = next;
	}

	specific->lba_prev = 0;
	specific->lba_next = 0;
}

/**
 * do we have any reclaim request that is pending for this
 * specific req to finish with its lba
 *
 */
bool_t
tm_is_reclaim_pending(req_t* lba_req){
	tm_specific_t* specific = TM_REQ_GET_SPECIFIC(lba_req);
	LOG("starting, for lba %u\n", specific->curr_lba);

	lba_req = specific->lba_next;
	if(lba_req == 0){
		LOG("no more reqs for this lba\n")
		return 0;
	}

	/* go backwards and find older read requests*/
	while(1){
		LOG("req %x\n", lba_req);
		specific = ((tm_specific_t*)(lba_req->specific));
		/* find rec req*/
		if(tm_is_reclaim_or_with_pending(lba_req)){
			LOG("found reclaim req %x\n", lba_req);
			return 1;
		}

		lba_req = specific->lba_next;
		if(lba_req == 0){
			return 0;
		}
	}

	assert(0);
	return 0;
}

/**
 * some request for an lba finished.
 * update all following reads
 *
 */
void
tm_update_pending_lba_reads(req_t* lba_req){
	tm_specific_t* specific = TM_REQ_GET_SPECIFIC(lba_req);
	uint8_t* orig_buf = specific->cdb->buf;
	req_t* next_req;
	LOG("starting, for lba %u\n", specific->curr_lba);

	lba_req = specific->lba_next;
	if(lba_req == 0){
		return;
	}
	specific = TM_REQ_GET_SPECIFIC(lba_req);

	/* go backwards and find older read requests*/
	while(specific->cdb->cmd == SCSI_READ){
		/*change state and copy buf*/
		LOG("updating READ req %x magic_num %u\n", lba_req, specific->cdb->magic_num);
		memcpy(specific->cdb->buf, orig_buf, TM_SECTOR_SIZE);
		specific->state = TM_TERMINATE; /*don't change enq reason, so we know to dequeue it due to missing resource*/

		lba_req = specific->lba_next;
		if(lba_req == 0){
			return;
		}
		specific = TM_REQ_GET_SPECIFIC(lba_req);
	}

	LOG("no more reads\n");
	return;
}

req_t*
tm_get_oldest_write(req_t* curr_req){
	tm_specific_t* orig_specific = TM_REQ_GET_SPECIFIC(curr_req);
	tm_specific_t* specific = orig_specific;
	req_t* req = curr_req;
	LOG("starting, for lba %u\n", orig_specific->curr_lba);

	/* if this is the first request*/
	if(tm_is_simple_write_req(req)){
		return req;
	}

	/* go backwards and find older request which is a write*/
	while(1){
		specific = TM_REQ_GET_SPECIFIC(req);
		LOG("req %x (curr_req %x) lba %u\n", req, curr_req, specific->curr_lba);
		if(tm_is_simple_write_req(req)){
			return req;
		}

		req = specific->lba_next;

		if(req == 0){
			return 0;
		}
	}
	LOG("none found. return 0\n");
	return 0;
}

req_t*
tm_get_recent_write(req_t* req){
	req_t* tm_req = 0;
//	tm_specific_t* orig_specific = ((tm_specific_t*)(req->specific));
	LOG("\n");
	while(req){
		req = tm_get_oldest_write(req);

		if(req !=0){
			tm_req = req;
			req = TM_REQ_GET_SPECIFIC(req)->lba_next;
		}
	}

	return tm_req;
}


/**
 * Check if general request queue contains any request for this lba
 */
req_t*
tm_get_last_req_for_lba(uint32_t lba){
	req_t* p = tm.queue;
	tm_specific_t* specific;
	LOG("check if empty\n");

	if(p==0){
		LOG("queue is empty. return\n");
		return 0;
	}

	/* FIFO queue. start from most ancient request */
	while(p->next != 0){
		specific = (tm_specific_t*)(p->specific);

		if(specific->curr_lba == lba){
			return p;
		}
		p = p->next;
	}

	return 0;
}


list_t*
tm_get_first_lba_list_node(uint32_t lba){
	return list_lookup(&(tm.handled_lba), lba);
}

bool_t
tm_is_lba_taken(req_t** first_lba_req, uint32_t lba){
	*first_lba_req = 0;LOG("starting\n");

	/* find in handled lba*/
	list_t* node = tm_get_first_lba_list_node(lba);
	if(node != 0){
		*first_lba_req = node->req;LOG("found req %x in handled_lba\n", *first_lba_req);
		return 1;
	}

	/* find in lba pending*/
	node = tm.lba_pending;
	while(node){
		if(TM_REQ_GET_SPECIFIC(node->req)->curr_lba == lba){
			assert(TM_REQ_GET_SPECIFIC(node->req)->lba_prev == 0); /*should be the first req for this lba*/
			*first_lba_req = node->req;LOG("found req %x in lba_pending\n", *first_lba_req);
			return 1;
		}

		node = node->next;
	}

	return 0;
}
/*********************************************
						 reclamation functions
 *********************************************/
void tm_init_rec_eu(uint32_t chip_idx){
	int i;
	LOG("starting\n");

	tm.rec_eu[chip_idx].address = TM_ADDR_EMPTY;
	tm.rec_eu[chip_idx].offset  = IDX_EMPTY;
	tm.rec_eu[chip_idx].blk_idx = IDX_EMPTY;

	for(i=0;i<NAND_TOTAL_SIZE;i++){
		tm.rec_eu[chip_idx].buf[i] = 0xff;
	}
}

void tm_clean_rec_eu(uint32_t chip_idx){
	int i;
	LOG("starting chip_idx %u\n", chip_idx);

	if(tm.rec_eu[chip_idx].blk_idx == IDX_EMPTY) return;

	tm.block[tm.rec_eu[chip_idx].blk_idx].state     = TM_BLOCK_EMPTY;
	tm.block[tm.rec_eu[chip_idx].blk_idx].obs_count = TM_SECTORS_PER_BLOCK;
	LOG("block %u obs_count is now %u\n", tm.rec_eu[chip_idx].blk_idx, tm.block[tm.rec_eu[chip_idx].blk_idx].obs_count);
	tm_init_rec_eu(chip_idx);
}

/**
 * choose block with most obsolete pages in a given chip to reclaim
 *
 * @return block index of chosen block in tm
 */
uint32_t
tm_choose_block_to_reclaim(uint32_t chip_idx){
	uint32_t i, obs_count = 0, blk_idx = IDX_EMPTY;
	LOG("starting\n");
	uint32_t chip_start_addr = tm_calc_chip_start_addr(chip_idx);
	uint32_t chip_end_addr   = tm_calc_chip_end_addr(chip_idx);
	uint32_t blk_idx_start   = chip_idx * TM_BLOCKS_PER_CHIP;
	uint32_t last_blk_ix_in_chip = blk_idx_start + TM_BLOCKS_PER_CHIP-1;
	tm_clean_rec_eu(chip_idx);
	tm.open_eu[chip_idx].in_reclaim = 1;

	/* find block to reclaim
	 * from the relevant chip*/
	LOG("tm.block[i].address %u, chip_start %u end %u\n", chip_start_addr, chip_end_addr);
	for(i=blk_idx_start; i<=last_blk_ix_in_chip; i++){
		LOG(" block %u, addr %u, state %u, obs_count %u, addr %u\n", i, tm.block[i].address, tm.block[i].state, tm.block[i].obs_count, tm.block[i].address);

#ifdef Fanatic
		if((tm.block[i].address < chip_start_addr || tm.block[i].address >= chip_end_addr)){
			printf("chip_start_addr %u, tm.block[%d].address %u, chip_end_addr %u\n", chip_start_addr, i, tm.block[i].address, chip_end_addr);
			assert((tm.block[i].address >= chip_start_addr && tm.block[i].address < chip_end_addr)); /*assert block is in chip*/
		}
		{
			/*assert obs_count matches bits*/
			uint32_t j, count = 0;
			for(j=0;j<NAND_PAGES_PER_ERASE_UNIT;j++){
				count += !(tm_bitmap_get(tm.block[i].address+j));
			}

			if(tm.block[i].obs_count != count){
				LOG("mismatch in obs count. bitmap count %u tm.block[%u].obs_count %u\n", count, i, tm.block[i].obs_count);
				for(j=0;j<NAND_PAGES_PER_ERASE_UNIT;j++){
					LOG("tm_bitmap_get(%u)=%u\n", tm.block[i].address+j, tm_bitmap_get(tm.block[i].address+j));
				}
				assert(tm.block[i].obs_count == count);
			}

		}
#endif
		/* choose block that was written in its entirety*/
		if(tm.block[i].obs_count > obs_count
			 &&
			 tm.block[i].state == TM_BLOCK_FULL){
			obs_count = tm.block[i].obs_count;
			blk_idx = i;
		}
	}

	assert(blk_idx != IDX_EMPTY);
	tm.rec_eu[chip_idx].address = tm.block[blk_idx].address;
	tm.rec_eu[chip_idx].offset  = 0;
	tm.rec_eu[chip_idx].blk_idx = blk_idx;

	LOG("chose block in idx %u to reclaim (blk addr %u)\n", blk_idx, tm.rec_eu[chip_idx].address);

	/* find empty block in chip*/
	blk_idx = IDX_EMPTY;
	for(i=blk_idx_start; i<=last_blk_ix_in_chip; i++){
		LOG("tm.block[%u].state %u\n", i, tm.block[i].state);
		if(tm.block[i].state == TM_BLOCK_EMPTY){
			blk_idx = i;
			break;/*stop searching */
		}
	}
	assert(blk_idx != IDX_EMPTY);
	assert(blk_idx != tm.rec_eu[chip_idx].blk_idx);
	LOG("will write to empty block idx %u to reclaim (blk addr %u)\n", blk_idx, tm.block[blk_idx].address);
	tm.open_eu[chip_idx].reclamation_in_progress = TM_PREPARE_TO_RECLAIM_REQ_MARKER; /* mark as non-zero. baRequest will put real req address here*/
	tm.open_eu[chip_idx].address = tm.block[blk_idx].address;
	tm.open_eu[chip_idx].offset  = 0;
	tm.open_eu[chip_idx].blk_idx = blk_idx;

	tm.block[tm.open_eu[chip_idx].blk_idx].state = TM_BLOCK_PARTIAL;
#ifdef Fanatic
	assert(tm.open_eu[chip_idx].address >= (chip_idx * NAND_PAGES_PER_CHIP)
			   &&
			   tm.open_eu[chip_idx].address < ((chip_idx+1) * NAND_PAGES_PER_CHIP));
#endif
	return blk_idx;
}

/*****************************************************
							auxiliary functions
 *****************************************************/
uint32_t
tm_get_open_eu_idx_by_page(uint32_t page_in_blk){
	page_in_blk &= NAND_EU_MASK;
	LOG("first block page %u\n", page_in_blk);
	uint32_t i;
	for(i=0; i< TM_OPEN_EUS;i++){
//		LOG("tm_get_open_eu_idx_by_page() - tm.open_eu[%u].address=%u\n", i, tm.open_eu[i].address);

		if(tm.open_eu[i].address == page_in_blk){
			LOG("return \n");
			return i;
		}
	}
	printf("could not find open eu for page %u", page_in_blk);
	assert(0);
	return IDX_EMPTY;
}

/**
 * find an empty block in given chip
 *
 * @return IDX_EMPTY if none found
 *         block idx in tm if found
 *
 */
uint32_t
tm_get_empty_eu(uint32_t chip_idx){
	uint32_t chip_start_addr, blk_idx, blk_idx_start;
	uint32_t last_blk_ix_in_chip;
	uint32_t n = 0;
	LOG("starting. chip_idx %u\n", chip_idx, last_blk_ix_in_chip);

	/*in reclaim mode. no point searching for empty eu*/
	if(tm.open_eu[chip_idx].in_reclaim){
		return IDX_EMPTY;
	}

	chip_start_addr = tm_calc_chip_start_addr(chip_idx);
	blk_idx_start   = chip_idx * TM_BLOCKS_PER_CHIP;
	last_blk_ix_in_chip = blk_idx_start + TM_BLOCKS_PER_CHIP-1;
	LOG("chip_start_addr %u. TM_BLOCKS_PER_CHIP %u NAND_BLOCKS_PER_CHIP %u\n",chip_start_addr, TM_BLOCKS_PER_CHIP, NAND_BLOCKS_PER_CHIP);

	for(blk_idx=blk_idx_start; blk_idx<last_blk_ix_in_chip; blk_idx++){
		LOG(" block %u state %u addr %u\n", blk_idx, tm.block[blk_idx].state, tm.block[blk_idx].address);

		if(tm.block[blk_idx].state == TM_BLOCK_EMPTY){
			n++;

			/* we want at least one empty block*/
			if(n<TM_MIN_EMPTY_BLOCKS_PER_CHIP){
				continue;
			}
			LOG("found empty block %u addr %u chip_start_addr %u \n", blk_idx, tm.block[blk_idx].address, chip_start_addr);
			LOG("NAND_PAGES_PER_BUS %u NAND_PAGES_PER_CHIP %u (chip_start_addr +NAND_PAGES_PER_CHIP) %u\n", NAND_PAGES_PER_CHIP, NAND_PAGES_PER_BUS, (chip_start_addr +NAND_PAGES_PER_CHIP));
			assert(tm.block[blk_idx].address >= chip_start_addr
					   &&
					   tm.block[blk_idx].address < (chip_start_addr +NAND_PAGES_PER_CHIP));
			return blk_idx;
		}
	}

	return IDX_EMPTY;
}

/**
 * is given chip free -
 * 1. no current
 * 2. not in reclamation || in reclamation and not waiting for a given req
 */
bool_t
tm_is_chip_free(uint32_t chip_idx,req_t* req){
	LOG("starting. chip_idx %u req %x\n", chip_idx, req);

	if(tm.open_eu[chip_idx].current !=0){
		LOG("not free. current %x\n", tm.open_eu[chip_idx].current);
		return 0;
	}

	if(tm.open_eu[chip_idx].reclamation_in_progress != 0
		 &&
		 tm.open_eu[chip_idx].current != req){
		LOG("req %x lba %u - chip not free. reclamation_in_progress %x current %u \n",
				req, TM_REQ_GET_SPECIFIC(req)->curr_lba, tm.open_eu[chip_idx].reclamation_in_progress, tm.open_eu[chip_idx].current);
		return 0;
	}

	LOG("chip %u is free\n", chip_idx);
	return 1;
}

/**
 * get rec_chip idx from a request.
 * it can be either from a reclaim request
 * or from a pending reclaim request
 */
tm_get_rec_chip_idx(tm_specific_t* specific){
	if (specific->rec_chip_idx != IDX_EMPTY){
		assert(tm_is_reclaim_req(specific));
		return specific->rec_chip_idx;
	}

	assert(!tm_is_reclaim_req(specific));
	if(specific->pending_reclaim_req != 0){
		return TM_REQ_GET_SPECIFIC(specific->pending_reclaim_req)->rec_chip_idx;
	}

	return IDX_EMPTY;
}

/****************************************************/
//bool_t
//tm_get_data_from_recent_write(req_t* req){LOG("specific->curr_lba %u READ. check for recent write req \n", TM_REQ_GET_SPECIFIC(req)->curr_lba);
//		req_t* first_lba_req;
//		assert(tm_is_lba_taken(&first_lba_req, TM_REQ_GET_SPECIFIC(req)->curr_lba));
//		req_t* write_req = tm_get_recent_write(first_lba_req); /* NOTICE - write, not reclaim write!!!!*/
//
//		/* no need to read chunk etc - read from recent_write, and finish*/
//		if(write_req !=0){LOG("specific->curr_lba %u READ. found recent write!. get data from it\n", TM_REQ_GET_SPECIFIC(req)->curr_lba);
////							LOG("((tm_specific_t*)(write_req->specific))->cdb->buf %x\n", ((tm_specific_t*)(write_req->specific))->cdb->buf);
//			memcpy(TM_REQ_GET_SPECIFIC(req)->cdb->buf, TM_REQ_GET_SPECIFIC(write_req)->cdb->buf, TM_SECTOR_SIZE);LOG("specific->curr_lba %u READ. do TM_DONE\n", TM_REQ_GET_SPECIFIC(req)->curr_lba);
//			TM_REQ_GET_SPECIFIC(req)->state = TM_DONE;
//			TM_REQ_GET_SPECIFIC(req)->enq_reason = TM_ENQUEUE_REASON_NONE;
//			TM_REQ_GET_SPECIFIC(req)->ooo_read = 1;
//			tm_list_pop(&(tm.resource_pending), req); /*so we don't get chosen by other request on unlock*/
//			//UNLOCK_TM_MUTEX();
//			(req->handler)(req,0);
//			return 1;
//		}
//
//		/*no prev write req to get data from*/
//		return 0;
//}

bool_t
tm_is_open_eu_full(uint32_t chip_idx){
	return (tm.open_eu[chip_idx].offset == NAND_PAGES_PER_ERASE_UNIT);
}

void
tm_update_chip(req_t* req){
	uint32_t chip_idx = TM_REQ_GET_SPECIFIC(req)->chip_idx;
	uint32_t blk_idx;

	tm.open_eu[chip_idx].offset++;

	if(!tm_is_open_eu_full(chip_idx)){
		return;
	}

	/* open eu is full*/
	/* get an other empty eu*/
	LOG("chip %u open_eu full. tm.open_eu[%u].blk_idx %u\n", chip_idx, chip_idx, tm.open_eu[chip_idx].blk_idx);
	tm.block[tm.open_eu[chip_idx].blk_idx].state = TM_BLOCK_FULL;
	blk_idx = tm_get_empty_eu(chip_idx);

	/* found empty block in this chip*/LOG("empty blk_idx %u\n", blk_idx);
	if(blk_idx != IDX_EMPTY){
		/*mark former block full*/
		tm.block[tm.open_eu[chip_idx].blk_idx].state = TM_BLOCK_FULL;

		/*get new empty block details*/
		tm.open_eu[chip_idx].blk_idx = blk_idx;
		tm.open_eu[chip_idx].address = tm.block[blk_idx].address;
		tm.open_eu[chip_idx].offset  = 0;
		tm.open_eu[chip_idx].current = 0;
		tm.block[blk_idx].state = TM_BLOCK_PARTIAL;
		return;
	}	LOG("no empty eu ~> perform reclamation (tm.open_eu[%u].offset %u)\n", chip_idx, tm.open_eu[chip_idx].offset);

	/* no empty eu ~> perform reclamation.
	 * NOTICE - need to regress this request to TM_LBA_INITIAL, since we can assure that
	 * until reclamation is over its chunk would still be cached*/
	/* this is where functionality kicks in - reclamation, erasures, etc...*/

	tm_choose_block_to_reclaim(chip_idx);
	tm.open_eu[chip_idx].current = TM_PREPARE_TO_RECLAIM_REQ_MARKER;LOG("tm.open_eu[%u].current = TM_PREPARE_TO_RECLAIM_REQ_MARKER\n", chip_idx);
}

bool_t
tm_req_triggered_reclaim(req_t* req){
	/* only write reqs can trigger reclamation*/LOG("starting.\n", req);
	if(TM_REQ_GET_SPECIFIC(req)->chip_idx == IDX_EMPTY){LOG("req with no chip (read?). not triggered\n");
		return 0;
	}LOG("req has chip (write?). chip_idx %u (current %x reclamation_in_progress %x)\n", TM_REQ_GET_SPECIFIC(req)->chip_idx, tm.open_eu[TM_REQ_GET_SPECIFIC(req)->chip_idx].current, tm.open_eu[TM_REQ_GET_SPECIFIC(req)->chip_idx].reclamation_in_progress);

	return (tm.open_eu[TM_REQ_GET_SPECIFIC(req)->chip_idx].current == TM_PREPARE_TO_RECLAIM_REQ_MARKER);
}

uint32_t tm_get_free_chip_idx(req_t* req){
	uint32_t chip_idx, i;LOG("starting req %x (pending_reclaim_req %x)\n", req, TM_REQ_GET_SPECIFIC(req)->pending_reclaim_req);

	/*req has pending reclaim (perhaps put there during chunk read)
	 * use its open eu*/
	if(TM_REQ_GET_SPECIFIC(req)->pending_reclaim_req != 0){
		req_t* pending_reclaim_req = TM_REQ_GET_SPECIFIC(req)->pending_reclaim_req;LOG("has pending reclaim %x. get rec_chip_idx %u\n", TM_REQ_GET_SPECIFIC(pending_reclaim_req), TM_REQ_GET_SPECIFIC(pending_reclaim_req)->rec_chip_idx);
		chip_idx = TM_REQ_GET_SPECIFIC(pending_reclaim_req)->rec_chip_idx;
		TM_REQ_GET_SPECIFIC(req)->chip_idx = chip_idx;LOG("return chip_idx %u\n", chip_idx);
		tm.open_eu[chip_idx].current = req;
		return chip_idx;
	}

	/* find a chip:
	 * 1. not handling request
	 * 2. not handling reclamation || reclaimed but waiting for this lba to finish)*/
	for(i=0;i<TM_OPEN_EUS;i++){
		chip_idx = (tm.last_chip_idx+i) % NAND_CHIP_COUNT;
		if(tm_is_chip_free(chip_idx, req)){
			tm.open_eu[chip_idx].current  = req;LOG("found empty chip %u. open_eu.blk_idx %u reclamation_in_progress %u, rec_eu.blk_idx %u addr %u\n",	chip_idx, tm.open_eu[chip_idx].blk_idx, tm.open_eu[chip_idx].reclamation_in_progress, tm.rec_eu[chip_idx].blk_idx, tm.rec_eu[chip_idx].address);
			TM_REQ_GET_SPECIFIC(req)->chip_idx = chip_idx;
//			/* if necessary, mark this write as out of order for pending_reclaim_req
//			 * so we know to dequeue pending_reclaim_req when finished*/
//			if(tm.open_eu[chip_idx].curr_lba == specific->curr_lba){LOG("tm.open_eu[%u].reclamation_in_progress %x\n", chip_idx, tm.open_eu[chip_idx].reclamation_in_progress);
//				assert(tm.open_eu[chip_idx].reclamation_in_progress);
//				TM_REQ_GET_SPECIFIC(req)->pending_reclaim_req = tm.open_eu[chip_idx].reclamation_in_progress;
//			}

			return chip_idx;
		}
	}

	return IDX_EMPTY;
}

bool_t
tm_is_waiting_for_chip(req_t* req){
	return (TM_REQ_GET_SPECIFIC(req)->enq_reason == TM_ENQUEUE_REASON_NO_CHIP);
}

bool_t
tm_is_lba_pending(req_t* req){
	return (TM_REQ_GET_SPECIFIC(req)->enq_reason == TM_ENQUEUE_REASON_LBA_TAKEN);
}

void
tm_activate_former_chip_pending(req_t* req, uint32_t idx){
	/*pop from pending lists*/LOG("starting. req %x, idx %u\n", req, idx);
//	tm_list_pop(&(tm.resource_pending), req);
	assert(req == tm_list_pop(&(tm.chip_pending), req));
	TM_REQ_GET_SPECIFIC(req)->enq_reason = TM_ENQUEUE_REASON_NONE;
	TM_REQ_GET_SPECIFIC(req)->chip_idx = idx;
}
///**
// * lookup req that its enq reason in no longer valid - a chip/lba is free
// * enqueue it and return it
// *
// */
//req_t*
//tm_list_lookup_req_with_available_resource(){
//	list_t* node;
//	tm_specific_t *specific;
//	req_t* req = 0;
//	uint32_t chip_idx;LOG("starting\n");
//
//	/*first, get chip_pending*/
//	node = tm.chip_pending;LOG("find free chips\n");
//	while(node){
//		req = node->req;
//		specific = TM_REQ_GET_SPECIFIC(req); LOG("req %x curr_lba %u magic_num %u\n", req, specific->curr_lba, specific->cdb->magic_num);assert(specific->enq_reason > TM_ENQUEUE_REASON_NONE);
//		chip_idx = tm_get_free_chip_idx(req);
//
//		if(chip_idx != IDX_EMPTY){LOG("req %x to free chip %u\n", req, chip_idx);
////			/*assign chip to req and vice-versa*/
////			tm.open_eu[chip_idx].current = req;
////			TM_REQ_GET_SPECIFIC(req)->chip_idx = chip_idx;
//			tm_activate_former_chip_pending(req, node);
//			return req;
//		}
//		else{LOG("req %x no free chip :( \n", req);
//			break;
//		}
//
//		node = node->next;
//	}
//
//	node = tm.lba_pending;LOG("find free lba's\n");
//	while(node){
//		req = node->req;
//		specific = TM_REQ_GET_SPECIFIC(req); //LOG("req %x curr_lba %u magic_num %u\n", req, specific->curr_lba, specific->cdb->magic_num);assert(specific->enq_reason > TM_ENQUEUE_REASON_NONE);
//
//		if(list_lookup(&(tm.handled_lba), specific->curr_lba) == 0){LOG("req %x lba %u magic_num %u not taken anymore\n", req, specific->curr_lba, specific->cdb->magic_num);
//			tm_activate_former_lba_pending(req);
//			return req;
//		}//LOG("req %x lba %u magic_num %u still taken :( \n", req, specific->curr_lba, specific->cdb->magic_num);
////		if(tm_is_lba_pending(req)){//LOG("req %x TM_ENQUEUE_REASON_LBA_TAKEN\n", req);
////			if(list_lookup(&(tm.handled_lba), specific->curr_lba) == 0){LOG("req %x lba %u magic_num %u not taken anymre\n", req, specific->curr_lba, specific->cdb->magic_num);
////				tm_activate_former_lba_pending(req, node);
////				return req;
////			}LOG("req %x lba %u magic_num %u still taken :( \n", req, specific->curr_lba, specific->cdb->magic_num);
////		}
////		else if(tm_is_waiting_for_chip(req)){//LOG("req %x TM_ENQUEUE_REASON_NO_CHIP\n", req);
////			/*do nothing. we know there is no free chip at this point*/
//////			chip_idx = tm_get_free_chip_idx(req);
//////			if(chip_idx != IDX_EMPTY){LOG("req %x to free chip %u\n", req, chip_idx);
//////				/*assign chip to req and vice-versa*/
//////				tm.open_eu[chip_idx].current = req;
//////				TM_REQ_GET_SPECIFIC(req)->chip_idx = chip_idx;
//////
//////				/*pop from pending lists*/
//////				tm_list_pop_specific(&(tm.resource_pending), node);
//////				tm_list_pop(&(tm.chip_pending), req);
//////				specific->enq_reason = TM_ENQUEUE_REASON_NONE;
//////				return req;
//////			}LOG("req %x no free chip :( \n", req);
////		}else{
////			assert(0);
////		}
//
//		node = node->next;
//	}
//
//	return 0;
//}
//
///**
// * handle a request handling an lba which is pending in chm
// * and tm_relcaim req is pending for
// */
//void
//tm_handle_reclaim_lba_chm_pending(req_t* first_lba_req, req_t* reclaim_req){//assert(first_lba_req->callee_req != 0);
//	TM_REQ_GET_SPECIFIC(first_lba_req)->pending_reclaim_req = reclaim_req;LOG("first_lba_req enq_reason %u. set pending_reclaim_req %x\n", TM_REQ_GET_SPECIFIC(first_lba_req)->enq_reason, reclaim_req);assert(TM_REQ_GET_SPECIFIC(first_lba_req)->enq_reason == TM_ENQUEUE_REASON_NONE);
//	tm.open_eu[TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx].current = 0; /*no more current. waiting for first_lba_req to finish*/
////	TM_REQ_GET_SPECIFIC(first_lba_req)->rec_chip_idx = TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx; /*mark as tm_reclaim*/
////	//UNLOCK_TM_MUTEX();
//
//	/**
//	 * NOTICE - tm lock released in chm, to avoid having chm_req processed and freed
//	 *          by another chip before this context can check it
//	 */
////	LOCK_CHM_MUTEX();/* do this here, since callee_req may be activated by the time we call chm_handle_tm_reclaim_lba_chm_pending */
//	chm_handle_tm_reclaim_lba_chm_pending(first_lba_req->callee_req, reclaim_req);
//}

bool_t
tm_assert_not_already_in_lba_pending(req_t *req, uint32_t lba){
	list_t *node = tm.lba_pending;
	while(node !=0){
		if(node->req == req){
			LOG("about to insert req %x to lba_pending, as pending for lba %u. req already in queue! (with lba %u)\n", req, lba, TM_REQ_GET_SPECIFIC(req)->curr_lba);
			return 0;
		}
		node = node->next;
	}

	return 1;
}

bool_t
tm_is_out_of_order_write(req_t* req){
	LOG("TM_REQ_GET_SPECIFIC(req)->pending_reclaim_req %x\n", TM_REQ_GET_SPECIFIC(req)->pending_reclaim_req);
	return (TM_REQ_GET_SPECIFIC(req)->pending_reclaim_req != 0);
}

/**
 * on terminate offset is NAND_PAGES_PER_ERASE_UNIT
 */
bool_t
tm_is_reclaim_done(req_t* reclaim_req){LOG("tm.rec_eu[%u].offset = %u\n", TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx, tm.rec_eu[TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx].offset);
	return (tm.rec_eu[TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx].offset == NAND_PAGES_PER_ERASE_UNIT);
}

/**
 * on terminate offset is IDX_EMPTY
 */
bool_t
tm_is_reclaim_terminated(req_t* reclaim_req){LOG("tm.rec_eu[%u].offset = %u\n", TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx, tm.rec_eu[TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx].offset);
	return (tm.rec_eu[TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx].offset == IDX_EMPTY);
}

/**
 * handle lba of req
 */
void
tm_terminate_lba_req(req_t* req){
	tm_specific_t* specific = TM_REQ_GET_SPECIFIC(req);

	/* reclamation is done*/
	if(specific->curr_lba == IDX_EMPTY){
		assert(tm_is_reclaim_req(specific));/*do nothing. reclaim is done*/
	}
	else if(specific->ooo_read == 0){
		/* req in handled_lba an done with this lba*/
		assert(!list_pop_specific(&(tm.handled_lba), specific->curr_lba, req));LOG("req %x (curr_lba %u) TM_TERMINATE\n", req, specific->curr_lba );
	}
	/*out of order read is not in handled_lba*/
	else{LOG("ooo_read. remove from lba queue\n");
		req_lba_list_pop_specific(req);
	}
}
/*****************************************************/
/* 				 		New code		  				 								 */
/*****************************************************/
void
tm_acquire_lba(uint32_t lba, req_t* req){LOG("starting req %x lba %u\n", req, lba);
	list_push(&(tm.handled_lba), lba, req);
	TM_REQ_GET_SPECIFIC(req)->state = TM_LBA_INITIAL;
}

bool_t
tm_get_data_from_recent_write(req_t* req){LOG("specific->curr_lba %u READ. check for recent write req \n", TM_REQ_GET_SPECIFIC(req)->curr_lba);
	req_t* first_lba_req;
	assert(tm_is_lba_taken(&first_lba_req, TM_REQ_GET_SPECIFIC(req)->curr_lba));
	req_t* write_req = tm_get_recent_write(first_lba_req); /* NOTICE - write, not reclaim write!!!!*/

	/* no need to read chunk etc - read from recent_write, and finish*/
	if(write_req !=0){LOG("specific->curr_lba %u READ. found recent write!. get data from it\n", TM_REQ_GET_SPECIFIC(req)->curr_lba);
//							LOG("((tm_specific_t*)(write_req->specific))->cdb->buf %x\n", ((tm_specific_t*)(write_req->specific))->cdb->buf);
		memcpy(TM_REQ_GET_SPECIFIC(req)->cdb->buf, TM_REQ_GET_SPECIFIC(write_req)->cdb->buf, TM_SECTOR_SIZE);LOG("specific->curr_lba %u READ. do TM_DONE\n", TM_REQ_GET_SPECIFIC(req)->curr_lba);
		TM_REQ_GET_SPECIFIC(req)->state = TM_DONE;
		TM_REQ_GET_SPECIFIC(req)->enq_reason = TM_ENQUEUE_REASON_NONE;
		TM_REQ_GET_SPECIFIC(req)->ooo_read = 1;
		//UNLOCK_TM_MUTEX();
		(req->handler)(req,0);
		return 1;
	}

	/*no prev write req to get data from*/
	return 0;
}


void
req_lba_list_push_as_next(req_t* p, req_t* req){
	tm_specific_t* specific;
	uint32_t orig_lba = TM_REQ_GET_SPECIFIC(p)->curr_lba;
	LOG("starting p %x req %x\n", p, req);

	/* put this req in end of this lba requests list*/
	specific = TM_REQ_GET_SPECIFIC(p);
	specific->lba_next = req;
	TM_REQ_GET_SPECIFIC(req)->lba_prev = p;
	TM_REQ_GET_SPECIFIC(req)->enq_reason = TM_ENQUEUE_REASON_LBA_TAKEN;LOG("req %x enq_reason TM_ENQUEUE_REASON_LBA_TAKEN \n", req);
}

//bool_t
//tm_is_reclaim_pending_for_write(req_t *reclaim_req){
//	return TM_REQ_GET_SPECIFIC(reclaim_req)->reclaim_pending_for_write;
//}

bool_t
tm_is_reclaimed_page_valid(req_t *reclaim_req){
	uint32_t chip_idx = TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx;

	return tm_bitmap_get(tm.rec_eu[chip_idx].address+tm.rec_eu[chip_idx].offset);
}

void
tm_activate_former_lba_pending(req_t* req){
	LOG("starting. req %x (lba %u)\n", req, TM_REQ_GET_SPECIFIC(req)->curr_lba);
	tm_list_pop(&(tm.lba_pending), req);

	/* a reclaim req that pended for a write will not handle this lba
	 * since a fresher copy of the lba page was given precedence (check if last known reclaimed page still valid)*/
	if(!tm_is_reclaim_req(TM_REQ_GET_SPECIFIC(req))
		 ||
		 tm_is_reclaimed_page_valid(req)){
		list_push(&(tm.handled_lba), TM_REQ_GET_SPECIFIC(req)->curr_lba, req);
//		TM_REQ_GET_SPECIFIC(reclaim_req)->reclaim_pending_for_write = 0;
	}
	TM_REQ_GET_SPECIFIC(req)->enq_reason = TM_ENQUEUE_REASON_NONE;
}


void
tm_handle_reclaim_lba_taken(req_t* first_lba_req, req_t* reclaim_req){LOG("starting first_lba_req %x reclaim_req %x\n", first_lba_req, reclaim_req);
  /* make second in line after first_lba_req
	 * when re-activated, reclaim req should be able to :
	 * 1. acquire lba - if following a read req
	 * 2. continue to next lba.
	 *
	 * NOTICE - if we're pending for a write req, we change state to move on to next lba. this is important to avoid
	 *          a false-valid page copying*/
	req_lba_list_push_as_next(first_lba_req, reclaim_req);
	TM_REQ_GET_SPECIFIC(reclaim_req)->state = (tm_is_read_req(first_lba_req))?TM_LBA_INITIAL:TM_RECLAIM_NEXT_LBA;
	TM_REQ_GET_SPECIFIC(reclaim_req)->enq_reason = TM_ENQUEUE_REASON_LBA_TAKEN;
#ifdef Fanatic
	LOG("reclaim_req %x enq_reason TM_ENQUEUE_REASON_LBA_TAKEN \n", reclaim_req);
	assert(tm_assert_not_already_in_lba_pending(reclaim_req, TM_REQ_GET_SPECIFIC(first_lba_req)->curr_lba));
#endif
	tm_list_push(&(tm.lba_pending), reclaim_req);

	/*mark relevant fields in first lba req*/
	TM_REQ_GET_SPECIFIC(first_lba_req)->pending_reclaim_req = reclaim_req;

	/* try to get from previous write*/
	if(tm_is_waiting_for_chip(first_lba_req)){LOG("first_lba_req waiting for chip\n");assert(!tm_is_read_req(first_lba_req));/*only a write is chip pending*/
		/*activate pedning write req.
		 * - mark as current req (for tm)
		 * - mark rec_chip_idx so its recognized as reclaim req by tm & chm
		 * - mark pending*/
		TM_REQ_GET_SPECIFIC(first_lba_req)->chip_idx = TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx;
		tm.open_eu[TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx].current = first_lba_req;
		return;
	}
	/*request pended for lba that was taken, but is no more*/
	else if(tm_is_lba_pending(first_lba_req)){
		if(!tm_is_read_req(first_lba_req)){
			TM_REQ_GET_SPECIFIC(first_lba_req)->chip_idx = TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx;
			tm.open_eu[TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx].current = first_lba_req;
		}
		return;
	}

	/* if we're pending for a write req, then we can move on to next */

	/*req in process. mark pending. best case - we wait for it to finish*/LOG("first_lba_req in process. mark pending, and wait for it to finish\n");
	tm.open_eu[TM_REQ_GET_SPECIFIC(reclaim_req)->rec_chip_idx].current = 0; /*no more current. waiting for first_lba_req to finish*/
}

/*****************************************************/
/* 				 		Functions		  				 								 */
/*****************************************************/
//void
//tm_debug_set_max_chips_full(){
//	uint32_t chip_start_addr, blk_idx, blk_idx_start;
//	uint32_t last_blk_ix_in_chip;
//	uint32_t n = 0;
//
//	uint32_t chip_idx;
//	for(chip_idx = 0; chip_idx < NAND_CHIP_COUNT;chip_idx++){
//		chip_start_addr = tm_calc_chip_start_addr(chip_idx);
//		blk_idx_start = chip_idx * TM_BLOCKS_PER_CHIP;
//		last_blk_ix_in_chip = blk_idx_start + TM_BLOCKS_PER_CHIP;
//		for(blk_idx=blk_idx_start; blk_idx<last_blk_ix_in_chip-1; blk_idx++){
//			tm.block[blk_idx].state = TM_BLOCK_FULL;printf("block[%u] is FULL (%u)\n", blk_idx, TM_BLOCK_FULL);
//		}
//	}
//}

void tmInit() {
	int i;
	uint32_t chip_idx, chip_start_addr, blk_idx, blk_idx_start;
	maintid = (int)pthread_self();
	LOG("starting\n");
#ifndef LOAD_STATE
	real_writes = 0;
	real_reads  = 0;
	reclaim_writes = 0;

	/*bitmap*/
	for(i=0;i<TM_BITMAP_SIZE;i++){
		tm.bitmap[i] = 0x00; // all free
	}

  /* allocate first blocks in every chip to chunk manager*/
	blk_idx = 0;
	printf("TM_SECTOR_COUNT %u, TM_BLOCKS_PER_CHIP %u, TM_SECTOR_BLOCKS_PER_CHIP %u\n", TM_SECTOR_COUNT, TM_BLOCKS_PER_CHIP, TM_SECTOR_BLOCKS_PER_CHIP);
	printf("TM_SECTOR_BLOCK_COUNT %u CH_BLOCK_COUNT %u TM_MIN_EMPTY_BLOCKS %u TM_MIN_SPARE_BLOCKS %u\n", TM_SECTOR_BLOCK_COUNT, CH_BLOCK_COUNT, TM_MIN_EMPTY_BLOCKS, TM_MIN_SPARE_BLOCKS);
  for(chip_idx = 0; chip_idx<NAND_CHIP_COUNT;chip_idx++ ){
		#ifdef PROFILING
		chip_writes[chip_idx] = 0;
		#endif
		chip_start_addr = tm_calc_chip_start_addr(chip_idx);
		printf("chip_start_addr %u\n",chip_start_addr);

		for(; blk_idx<(chip_idx+1) * TM_BLOCKS_PER_CHIP; blk_idx++){
			tm.block[blk_idx].address = chip_start_addr+ (blk_idx % TM_BLOCKS_PER_CHIP) * NAND_PAGES_PER_ERASE_UNIT;
			printf("tm.block[%u].address %u\n", blk_idx, tm.block[blk_idx].address);
			tm.block[blk_idx].obs_count = TM_SECTORS_PER_BLOCK;
			tm.block[blk_idx].state     = TM_BLOCK_EMPTY;
		}

		/* "open" EU(s)*/
		tm.open_eu[chip_idx].blk_idx = tm_get_empty_eu(chip_idx);assert(tm.open_eu[chip_idx].blk_idx != IDX_EMPTY);
		tm.open_eu[chip_idx].address = tm.block[tm.open_eu[chip_idx].blk_idx].address;
		tm.open_eu[chip_idx].offset  = 0;
		tm.open_eu[chip_idx].current  = 0;
		tm.open_eu[chip_idx].in_reclaim = 0;

		tm_init_rec_eu(chip_idx);
	}
  tm.last_chip_idx = 0;/* start writing from chip 0*/
#endif

	/*mutexes*/
	pthread_mutex_init(&(tm.defer_mutex), NULL);
	tm.tmDeferred = 0;

	pthread_mutex_init(&(tm.mutex), 0);
	pthread_cond_init (&(tm.cond ), 0);
	tm.mutex_counter = 0;
	tm.lba_pending = 0;
	tm.handled_lba = 0;
}

void tmHandler(req_t* req, void* response) ;

/**
 * allocate request with tm_specific_t data
 */
void tmAlloc(req_t* caller_req) {
  req_t* req;
  req = (req_t*) malloc(sizeof(req_t));

  /* start allocating*/
  tm_specific_t* specific;
  specific = (tm_specific_t*) malloc(sizeof(tm_specific_t));

  /* allocation successful*/
  if (req != 0 && specific != 0) {
    req->handler     = tmHandler;
    req->caller_req  = caller_req;
    req->specific    = specific;
    req->callee_req  = 0; // no calle req yet
    req->next        = 0; // not in any list
    req->prev        = 0; // not in any list
//    LOG("tmAlloc() - call caller handler\n");
    (caller_req->handler)(caller_req, req);
    return;
  }

  /* allocation failure - defer*/
  if (req != 0)      free(req);
  if (specific != 0) free(specific);

  pthread_mutex_lock(&(tm.defer_mutex));
  caller_req->prev = tm.tmDeferred;
  caller_req->next = 0;
  if(tm.tmDeferred != 0) tm.tmDeferred->next = caller_req;
  tm.tmDeferred = caller_req;
  pthread_mutex_unlock(&(tm.defer_mutex));

  return;
}

error_t tmRequest(req_t *req,
                  cdb_t* cdb,
                  uint32_t rec_chip_idx) {
  tm_specific_t* specific = (tm_specific_t*) req->specific;

// // LOCK_TM_MUTEX();

  specific->cdb      = cdb;
  specific->curr_lba = specific->cdb->start_lba;
  specific->state    = TM_INITIAL; /* regular requets starts here*/
  specific->chip_idx = IDX_EMPTY; /*by default empty*/
  /* if this a reclaim request
   * use rec_eu buffer for mock cdb buffer*/
  if(rec_chip_idx != IDX_EMPTY) {
  	specific->state    = TM_RECLAIM_START;
  	specific->cdb->buf = tm.rec_eu[rec_chip_idx].buf;
  	LOG("tm.open_eu[%u].current = TM_PREPARE_TO_RECLAIM_REQ_MARKER\n", rec_chip_idx);
  	assert(tm.open_eu[rec_chip_idx].current == TM_PREPARE_TO_RECLAIM_REQ_MARKER);
  	tm.open_eu[rec_chip_idx].current = req;
  	tm.open_eu[rec_chip_idx].reclamation_in_progress = req;
  	specific->chip_idx = rec_chip_idx;
  }
  specific->rec_chip_idx = rec_chip_idx;
  specific->is_page_read = 0;

  specific->lba_next = 0;
  specific->lba_prev = 0;

  specific->page = TM_ADDR_EMPTY;
//  specific->last_known_written_page = TM_ADDR_EMPTY;
  specific->pending_reclaim_req = 0;
  specific->enq_reason = TM_ENQUEUE_REASON_NONE;
  specific->ooo_read = 0;
//  specific->reclaim_pending_for_write = 0;

  //  LOG("tmRequest() - call tmHandler\n");

  int i;
  for(i=0;i<NAND_SPARE_SIZE;i++) specific->spare[i] = 0xff;

  (req->handler)(req,0);
}

void tmHandler(req_t* req, void* response) {
  tm_specific_t* specific = TM_REQ_GET_SPECIFIC(req);
  req_t *nandbus_req, *chm_req, *dq_req = 0, *last_lba_req, *write_req;
	uint32_t bus;
	uint32_t chip;
	uint32_t page;
	uint32_t offset;
	uint32_t len;
	int i;
	uint8_t chm_cmd;
	uint32_t chip_idx;
	uint32_t blk_idx;
	cdb_t *new_cdb;
	list_t* first_lba_list_node;
	uint32_t new_addr;
	bool_t need_to_rec_marker;
	bool_t is_read_req;
	bool_t send_write_req_to_chip = 0;
	req_t* dequeued[NAND_CHIP_COUNT];
	req_t* first_lba_req;

	LOG("speciflc->curr_lba %u (%s), magic_num %u. req %x, state %u pending_reclaim_req %x rec_chip_idx %u (mutex count %u)\n", specific->curr_lba, (specific->cdb->cmd == SCSI_READ)?"READ":"WRITE", specific->cdb->magic_num, req, specific->state, specific->pending_reclaim_req, specific->rec_chip_idx, tm.mutex_counter);

	switch (specific->state) {
		case TM_INITIAL:
		  /* can't overflow sector count */LOG("\n");
			if(specific->curr_lba>= TM_SECTOR_COUNT){
				LOG("lba %u. TM_BLOCK_COUNT %u, CH_BLOCK_COUNT %u, CH_MAX_CHUNK_COUNT %u\n",specific->curr_lba,TM_BLOCK_COUNT, CH_BLOCK_COUNT, CH_MAX_CHUNK_COUNT);
				printf("speciflc->curr_lba %u (%s), magic_num %u. req %x, state %u pending_reclaim_req %x rec_chip_idx %u (mutex count %u)\n", specific->curr_lba, (specific->cdb->cmd == SCSI_READ)?"READ":"WRITE", specific->cdb->magic_num, req, specific->state, specific->pending_reclaim_req, specific->rec_chip_idx, tm.mutex_counter);
				printf("lba %u. TM_BLOCK_COUNT %u, CH_BLOCK_COUNT %u, CH_MAX_CHUNK_COUNT %u\n",specific->curr_lba,TM_BLOCK_COUNT, CH_BLOCK_COUNT, CH_MAX_CHUNK_COUNT);
				assert(specific->curr_lba < TM_SECTOR_COUNT);
			}

		  /* if lba in use, and not by this request - queue it.
		   * otherwise - list it*/
//		  LOG("mutex+queue_mutex lock\n");
		 // LOCK_TM_MUTEX();
		  specific->state = TM_ACQUIRE_LBA;
		case TM_ACQUIRE_LBA:
		  /* can't proceed normally - lba taken by prev req */LOG("tm_is_lba_taken\n");
		  if(tm_is_lba_taken(&first_lba_req, specific->curr_lba) != 0){LOG("lba taken by %x\n", first_lba_req);
		  	/* read */
		  	if(tm_is_read_req(req)){LOG("read req\n");
					specific->state = TM_READ_LBA_TAKEN;
					req_lba_list_push(first_lba_req, req);
					(req->handler)(req,0);
					break;
				}

		  	/* write */
		  	else if(tm_is_simple_write_req(req)){LOG("write req\n");
		  		specific->state = TM_LBA_TAKEN;
		  		req_lba_list_push(first_lba_req, req);
					(req->handler)(req,0);
					break;
				}

		  	/* reclaim */
		  	else if(tm_is_reclaim_req(TM_REQ_GET_SPECIFIC(req))){LOG("reclaim req\n");
		  		tm_handle_reclaim_lba_taken(first_lba_req, req);
					break;
				}
		  	assert(0);/* no other option!*/
		  }
		  /* lba not in use at all. push it to list */
		  /* NOTICE if lba in use by this request - no need to list it, we simply continue! */
		  /* new req for this lba. start executing*/LOG(" specific->curr_lba %u, TM_INITIAL. push lba to list!\n", specific->curr_lba);
			assert(specific->lba_prev == 0); /* assert there's no previous request for this lba*/
			tm_acquire_lba(specific->curr_lba, req);
			req_activate(req, 0);
			break;
		case TM_READ_LBA_TAKEN:
			if(tm_get_data_from_recent_write(req)){
				break;
			}LOG("specific->curr_lba %u READ. no recent write. move on\n", specific->curr_lba);
		case TM_LBA_TAKEN:
			/* can't process. queue*/
			specific->state = TM_LBA_INITIAL; /*lba is  acquired on dequeue*/
			specific->enq_reason = TM_ENQUEUE_REASON_LBA_TAKEN;LOG("req %x enq_reason TM_ENQUEUE_REASON_LBA_TAKEN \n", req);
#ifdef Fanatic
			assert(tm_assert_not_already_in_lba_pending(req, TM_REQ_GET_SPECIFIC(req)->curr_lba));
#endif
			tm_list_push(&(tm.lba_pending), req);LOG("specific->curr_lba %u WRITE lba taken ENQUEUE\n", specific->curr_lba);
			//UNLOCK_TM_MUTEX();
			break;
		case TM_LBA_INITIAL: /* some lba*/LOG("TM_LBA_INITIAL. specific->curr_lba %u\n", specific->curr_lba);
			/* regardless of SCSI_READ/WRITE we need to get the chunk to RAM*/
			specific->state = TM_CHM_READ_READY;
			chmAlloc(req);
			break;
		case TM_CHM_READ_READY:
			specific->state = TM_CHM_READ_DONE;
			chm_req = (req_t*) response;
			req->callee_req = chm_req;
			chm_cmd = (specific->cdb->cmd == SCSI_READ)?CHM_READ:CHM_READ_BEFORE_WRITE;
			//UNLOCK_TM_MUTEX(); /*unlock before transfering control to an other module*/
			chmRequest(chm_req,
								 chm_cmd,
								 specific->cdb->start_lba,
								 0,
								 HINT_FLAG_NO);
			break;
		case TM_CHM_READ_DONE:
//			LOG("mutex lock\n");
			//LOCK_TM_MUTEX();LOG("TM_CHM_READ_DONE starting. specific->curr_lba %u\n", specific->curr_lba);
			/*no chm callee anymore*/
			if(req->callee_req != 0){LOG("TM_CHM_READ_DONE. req->callee_req %x, nullify\n", req->callee_req);
				req->callee_req = 0;
			}

//			if(specific->is_page_read == 0){ /*XXX do we need this field anymore?*/
//				specific->page  = *((uint32_t*)(response));LOG("specific->page %u\n", specific->page);
////				specific->last_known_written_page = specific->page;LOG("specific->curr_lba %u. got page addr %u (%x) from chunk\n", specific->curr_lba, specific->page, specific->page);
//				if(specific->page != TM_ADDR_EMPTY){
//					assert(specific->page < NAND_PAGE_COUNT );
//				}
//				specific->is_page_read = 1;
//			}
			specific->page  = *((uint32_t*)(response));

			/* SCSI_READ - read the page and get this over with*/
			if(specific->cdb->cmd == SCSI_READ){LOG("specific->curr_lba %u (READ), TM_CHM_READ_DONE. specific->page %u\n", specific->curr_lba, specific->page);
                                real_reads++;
				/* if address is empty mock-read it*/
				if(specific->page == TM_ADDR_EMPTY){
					memset(specific->cdb->buf, 0xff, NAND_PAGE_SIZE);
					specific->state = TM_DONE;
					//UNLOCK_TM_MUTEX();
					req_activate(req,0);
					break;
				}

				specific->state = TM_NANDBUS_READY;
				//UNLOCK_TM_MUTEX();
				nandbusAlloc(req);
				break;
			}

			/* SCSI_WRITE - preliminary chunk read done. continue*/
			LOG("specific->curr_lba %u (WRITE), TM_CHM_READ_DONE. write\n", specific->curr_lba);
			/*is this a reclaim request?
			 * if so, stop treating it as regular, and continue straight to write
			 * since lba is taken by reclaim and we can definitely do this*/
			if(tm_is_reclaim_req(specific)){LOG("TM_CHM_READ_DONE. this is a reclaim request\n");
				/*continue to writing (we definitely have an open eu)*/
				/* obsolete old*/
				tm_bitmap_set(specific->page, 0); /* old obsolete*/

				page = tm.open_eu[specific->rec_chip_idx].address + tm.open_eu[specific->rec_chip_idx].offset;
				LOG("TM_CHM_READ_DONE. continue writing. call TM_NANDBUS_READY page %u\n", page);
				specific->state = TM_NANDBUS_READY;
				specific->page = page;
				((uint32_t*)(&(specific->spare[NAND_SPARE_LBA_OFFSET])))[0] = specific->curr_lba;
				LOG("mutex unlock\n");
				reclaim_writes++;
				//UNLOCK_TM_MUTEX();
				nandbusAlloc(req);
				break;
			}

			/* regular request*/
			/* if old even existed, obsolete it*/
			if(specific->page != TM_ADDR_EMPTY){
				tm_bitmap_set(specific->page, 0); /* old obsolete*/
			}

			specific->state = TM_WRITE_READY;LOG("call TM_WRITE_READY\n");
//			//UNLOCK_TM_MUTEX();//				/* commented for new relcima stuff*/
			req_activate(req,0);
			break;
		case TM_WRITE_READY:
			/* to avoid starvation, see if we have any open_eu pending requests.
			 * if so, and this is not a reclamtion req
			 * enqueue it*/
			if(tm.chip_pending != 0){
				if(!tm_is_reclaim_or_with_pending(req)){
					tm_list_push(&(tm.chip_pending), req);LOG("push to chip_pending\n");
					specific->enq_reason = TM_ENQUEUE_REASON_NO_CHIP;LOG("push to chip_pending\n");
					break;
				}
			}
			LOG("TM_WRITE_READY starting\n");
			//			{
			//				/* print current for all chips*/
			//				for(i=0;i<TM_OPEN_EUS;i++){
			//					LOG("tm.open_eu[%u].current %x\n", i ,tm.open_eu[i].current);
			//				}
			//			}LOG("TM_WRITE_READY find free chip_idx\n");
			/* try finding an open eu that's not handling a write.
			 * find relevant chip in round-robin style*/
			if(specific->chip_idx == IDX_EMPTY){
				chip_idx =tm_get_free_chip_idx(req);LOG("chip_idx %u\n", chip_idx);
			}
			specific->state = TM_CHIP_FOUND; /*in either, when we continue we have a chip!*/
			/*none found. queue request*/
			if(chip_idx == IDX_EMPTY){
				specific->enq_reason = TM_ENQUEUE_REASON_NO_CHIP;LOG("push to chip_pending\n");
				tm_list_push(&(tm.chip_pending), req);LOG("push to chip_pending\n");
				LOG("specific->curr_lba %u, TM_WRITE_READY. no free chip (chip[0].current %x\n",
						specific->curr_lba, tm.open_eu[0].current);
				if(tm.open_eu[0].current !=0 && tm.open_eu[0].current != TM_PREPARE_TO_RECLAIM_REQ_MARKER){LOG("curr_lba %u magic_num %u state %u)", TM_REQ_GET_SPECIFIC(tm.open_eu[0].current)->curr_lba, TM_REQ_GET_SPECIFIC(tm.open_eu[0].current)->cdb->magic_num, TM_REQ_GET_SPECIFIC(tm.open_eu[0].current)->state);}LOG("ENQUEUE\n\n\n\n");
//				reqEnqueue(req, &(tm.queue));
				//UNLOCK_TM_MUTEX();
				break;
			}

			specific->chip_idx = chip_idx;
			/*chip allocated normally*/
			/*dont forget to move to next chip */
			if(!tm_is_out_of_order_write(req)){LOG("not out of order write\n");
				tm.last_chip_idx++;
				tm.last_chip_idx = tm.last_chip_idx % NAND_CHIP_COUNT;
			}
		case TM_CHIP_FOUND:
			/* we can always write at this point, even upon returning from queue*/
			chip_idx = specific->chip_idx;LOG("specific->curr_lba %u, prepare to write chip_idx %u\n", specific->curr_lba , chip_idx);assert(chip_idx != IDX_EMPTY);
			#ifdef PROFILING
			chip_writes[chip_idx]++;
			#endif
			assert(program[tm.open_eu[chip_idx].address+tm.open_eu[chip_idx].offset] == 0);LOG("specific->curr_lba %u, prepare to write chip_idx %u. curr open eu addr %u offset %u\n", specific->curr_lba , chip_idx, tm.open_eu[chip_idx].address, tm.open_eu[chip_idx].offset);
			specific->state = TM_NANDBUS_READY;

			page = tm.open_eu[chip_idx].address + tm.open_eu[chip_idx].offset;
			specific->page = page;LOG("specific->curr_lba %u, TM_CHM_READ_DONE. unlock tm mutex before nandbusAlloc\n", specific->curr_lba);
			real_writes++;
			//UNLOCK_TM_MUTEX();
			nandbusAlloc(req);
			break;
		case TM_RECLAIM_START:
		case TM_RECLAIM_NEXT_LBA:
			specific->curr_lba = IDX_EMPTY;
			specific->cdb->start_lba = specific->curr_lba;LOG("TM_RECLAIM_NEXT_LBA. start reclaiming\n");
			assert(req->caller_req->handler == baHandler);
			specific->pending_reclaim_req = 0;/* just in case...*/

			/*skip obsolete pages*/
			page = tm.rec_eu[specific->rec_chip_idx].address+tm.rec_eu[specific->rec_chip_idx].offset;LOG("page %u\n", page);
			while(tm.rec_eu[specific->rec_chip_idx].offset < NAND_PAGES_PER_ERASE_UNIT
					  &&
					  !tm_bitmap_get(page)){
//				LOG("page %u is obsolete\n", tm.rec_eu[specific->rec_chip_idx].address+tm.rec_eu[specific->rec_chip_idx].offset);
				tm.rec_eu[specific->rec_chip_idx].offset++;
				page = tm.rec_eu[specific->rec_chip_idx].address+tm.rec_eu[specific->rec_chip_idx].offset;
			}

			/* are we done?*/
			if(tm_is_reclaim_done(req)){LOG("no more pages to reclaim in block. call TM_RECLAIM_DONE\n");
				specific->state = TM_RECLAIM_DONE;
				req_activate(req,0);
				break;
			}LOG("reclaim page %u\n", page);

			/* read page so we get it's lba*/
			/* NOTICE - its possible that a future req will render this page obsolete.
			 * we deal with this in TM_RECLAIM_READ_DONE*/
			specific->state = TM_RECLAIM_READ_READY;
			req_activate(req,0);
			break;
		case TM_RECLAIM_READ_READY:
			LOG("TM_RECLAIM_READ_READY. starting\n");
			assert(req->caller_req->handler == baHandler);
			specific->state = TM_RECLAIM_NANDBUS_READY;
			reclaim_reads++;
			LOG("mutex unlock\n");
			//UNLOCK_TM_MUTEX();
			nandbusAlloc(req);
			break;
		case TM_RECLAIM_NANDBUS_READY:
			LOG("TM_RECLAIM_NANDBUS_READY. specific->curr_lba %u\n", specific->curr_lba);
			assert(req->caller_req->handler == baHandler);
			/* do spare read/page write*/
			nandbus_req = (req_t*) response;
			specific->state = TM_RECLAIM_READ_DONE;

			/* get physical page address*/
			page = tm.rec_eu[specific->rec_chip_idx].address+tm.rec_eu[specific->rec_chip_idx].offset;LOG("tm.rec_eu[%u].address %u offset %u\n", specific->rec_chip_idx, tm.rec_eu[specific->rec_chip_idx].address, tm.rec_eu[specific->rec_chip_idx].offset);
			specific->page = page;LOG("TM_RECLAIM_NANDBUS_READY set specific->page %u\n", specific->page);

			/* do nandbusCmd*/
//			LOG("specific->curr_lba %u, TM_RECLAIM_NANDBUS_READY. %s of page %u specific->cdb->buf %x\n",
//					specific->curr_lba , (specific->cdb->cmd == SCSI_READ)?"READ":"WRITE", page, specific->cdb->buf);
			LOG("TM_RECLAIM_NANDBUS_READY. specific->curr_lba %u, page %u bus %u chip %u. \n",
					specific->curr_lba, page, page / NAND_PAGES_PER_BUS, (page % NAND_PAGES_PER_BUS) / NAND_PAGES_PER_CHIP);

			nandbusCmd(nandbus_req,
				 /* buf*/specific->cdb->buf,
			/* bus*/   page / NAND_PAGES_PER_BUS,
			/* chip*/  (page % NAND_PAGES_PER_BUS) / NAND_PAGES_PER_CHIP,
			/* cmd*/	 NAND_CMD_READ,
			/* page*/	 page,
	/* offset */	 0,
	     /* len*/  NAND_TOTAL_SIZE,
		 /*spare*/   specific->spare,
/*with spare*/	 1);
			break;
		case TM_RECLAIM_READ_DONE:
			//LOCK_TM_MUTEX();LOG("TM_RECLAIM_READ_DONE. starting\n");

			assert(req->caller_req->handler == baHandler);
			/* get lba, and continue to write?*/
			specific->curr_lba = ((uint32_t*)(&(specific->spare[NAND_SPARE_LBA_OFFSET])))[0];LOG("TM_RECLAIM_READ_DONE. got page %u specific->curr_lba %u\n", specific->page, specific->curr_lba);
			assert(specific->curr_lba != TM_ADDR_EMPTY);

			/* page invalidated by some other write
			 * while chip read it*/
			if(!tm_bitmap_get(specific->page)){
				tm.rec_eu[specific->rec_chip_idx].offset++;
				specific->state = TM_RECLAIM_NEXT_LBA;
				req_activate(req,0);
				break;
			}

			chip_idx = specific->rec_chip_idx;
			specific->cdb->start_lba = specific->curr_lba;

			/* read chunk, so we can later update it
			 * for this stage we can treat req as regular*/
			specific->state = TM_ACQUIRE_LBA;

//			//UNLOCK_TM_MUTEX();
			req_activate(req,0);
			break;
		case TM_RECLAIM_DONE:
			LOG("TM_RECLAIM_DONE. starting\n");
			assert(req->caller_req->handler == baHandler);

			/* erase old eu*/
			specific->state = TM_RECLAIM_ERASE_READY;
			specific->page  = tm.rec_eu[specific->rec_chip_idx].address;
			//UNLOCK_TM_MUTEX();
			nandbusAlloc(req);
			break;
		case TM_RECLAIM_ERASE_READY:
			LOG("TM_RECLAIM_NANDBUS_READY. specific->curr_lba %u\n", specific->curr_lba);
			assert(req->caller_req->handler == baHandler);
			/* do spare read/page write*/
			nandbus_req = (req_t*) response;
			specific->state = TM_RECLAIM_ERASE_DONE;

			/* do nandbusCmd*/
			page = specific->page;LOG("TM_RECLAIM_ERASE_READY. erase of page %u\n", specific->page);
			nandbusCmd(nandbus_req,
				 /* buf*/0,
			/* bus*/   page / NAND_PAGES_PER_BUS,
			/* chip*/  (page % NAND_PAGES_PER_BUS) / NAND_PAGES_PER_CHIP,
			/* cmd*/	 NAND_CMD_ERASE,
			/* page*/	 page,
	/* offset */	 0,
			 /* len*/  0,
								 0, 0);
			break;
		case TM_RECLAIM_ERASE_DONE:
			/*update details of open block with that of newly reclaimed blcok*/
			//LOCK_TM_MUTEX();assert(req->caller_req->handler == baHandler);LOG("TM_RECLAIM_ERASE_DONE\n");
			blk_idx = tm.rec_eu[specific->rec_chip_idx].blk_idx;
			tm.open_eu[specific->rec_chip_idx].current  = 0;
			tm.open_eu[specific->rec_chip_idx].reclamation_in_progress = 0;LOG("new open eu blk_idx %u addr %u \n", tm.open_eu[specific->rec_chip_idx].blk_idx, tm.open_eu[specific->rec_chip_idx].address);

			tm.block[blk_idx].state = TM_BLOCK_EMPTY;
			tm.block[blk_idx].obs_count = TM_SECTORS_PER_BLOCK;LOG("block %u obs_count is now %u\n", blk_idx, tm.block[blk_idx].obs_count);
			tm_init_rec_eu(specific->rec_chip_idx);

			specific->state = TM_DONE;
			req_activate(req,0);
			break;
		case TM_NANDBUS_READY:
			nandbus_req = (req_t*) response;
			/*read - we are done
			 * write - still need to update chunk*/
			specific->state = (specific->cdb->cmd == SCSI_READ)?TM_DONE:TM_NAND_WRITE_DONE;

			/* get physical page address*/
			page = specific->page;LOG("specific->curr_lba %u, TM_NANDBUS_READY. specific->page=%u. next state %u\n", specific->curr_lba , specific->page, specific->state);

			/* do nandbusCmd*/
			LOG("specific->curr_lba %u, TM_NANDBUS_READY. %s of page %u specific->cdb->magic_num %u. mark lba in spare and call nandbusCmd\n",
					specific->curr_lba , (specific->cdb->cmd == SCSI_READ)?"READ":"WRITE", page, specific->cdb->magic_num);
			((uint32_t*)(&(specific->spare[NAND_SPARE_LBA_OFFSET])))[0] = specific->curr_lba;
//			LOG("page %u bus %u chip %u. NAND_PAGES_PER_BUS %u, NAND_PAGES_PER_CHIP %u\n",
//					page / NAND_PAGES_PER_BUS, page / NAND_PAGES_PER_BUS,(page % NAND_PAGES_PER_CHIP) / NAND_PAGES_PER_CHIP, NAND_PAGES_PER_BUS, NAND_PAGES_PER_CHIP);
//			LOG("here\n");
//			LOG("specific->cdb->buf %x \n", specific->cdb->buf);
//			LOG("page %u \n", page);
//			LOG("specific->spare %x \n", specific->spare);
//			LOG("nandbus_req %x \n", nandbus_req);
			nandbusCmd(nandbus_req,
				 /* buf*/specific->cdb->buf,
			/* bus*/  page / NAND_PAGES_PER_BUS,
			/* chip*/ (page % NAND_PAGES_PER_BUS) / NAND_PAGES_PER_CHIP,
			/* cmd*/	(specific->cdb->cmd == SCSI_READ)?NAND_CMD_READ:NAND_CMD_WRITE,
			/* page*/	page,
	/* offset */	0,
	     /* len*/ NAND_PAGE_SIZE,
							  specific->spare, 1);
//			LOG("tmHandler() - specific->curr_lba %u, TM_NANDBUS_READY. nandbusCmd done\n", specific->curr_lba , page);
			break;
		case TM_NAND_WRITE_DONE:
			/*increment open eu offset*/
			LOG("specific->curr_lba %u, TM_NAND_WRITE_DONE\n", specific->curr_lba );
			chip_idx = specific->chip_idx;
			assert(((uint32_t*)(&(specific->spare[NAND_SPARE_LBA_OFFSET])))[0] == specific->curr_lba);
			tm_bitmap_set(specific->page, 1);LOG("specific->curr_lba %u, TM_NAND_WRITE_DONE. inc offset. specific->page %u\n", specific->curr_lba , specific->page);
//			tm.open_eu[chip_idx].offset++;LOG("specific->curr_lba %u, TM_NAND_WRITE_DONE. new open_eu offset %u.\n", specific->curr_lba, tm.open_eu[chip_idx].offset);

			/*update relevant chunk*/
			specific->state = TM_CHM_WRITE_READY;
			LOG("specific->curr_lba %u, TM_NAND_WRITE_DONE. call chmAlloc()\n", specific->curr_lba );
			chmAlloc(req);
			break;
		case TM_CHM_WRITE_READY:
			/* tell chm to update chunk with new page address*/
			specific->state = TM_DONE;
			chm_req = (req_t*)(response);
			req->callee_req = chm_req;
			//UNLOCK_TM_MUTEX(); /*unlock before transfering control to an other module*/
			chmRequest(chm_req,
								 CHM_WRITE,
								 specific->cdb->start_lba,
								 specific->page,
								 HINT_FLAG_NO);
			break;
		case TM_DONE:
			/* finished with request!*/
			//LOCK_TM_MUTEX();LOG("req %x (curr_lba %u chip_idx %u) TM_DONE\n", req, specific->curr_lba, specific->chip_idx );

		  /* finalize write*/
		  if(tm_is_write_req(req)){LOG("write req done. specific->chip_idx %u\n", specific->chip_idx);assert(specific->chip_idx >= 0);
				/* handle lba in different lists*/
				tm_terminate_lba_req(req);

		  	/*clear chip so we know an other write can proceed*/
		  	tm.open_eu[specific->chip_idx].current = 0;

		  	/* update relevant chip*/
//		  	if(tm_is_reclaim_req(specific)
//		  		 &&
//		  		 tm_is_reclaim_done(req))
		  	tm_update_chip(req);
		  }

			specific->state = TM_TERMINATE;
		case TM_TERMINATE: /*need this state for read requests that are finished without being actually processed (in-advance read)*/
			LOG("TM_TERMINATE. req->callee_req %x, pending_reclaim_req %x\n", req->callee_req, specific->pending_reclaim_req);
			if(req->callee_req != 0){LOG("TM_TERMINATE. req->callee_req %x, nullify\n", req->callee_req);
				req->callee_req = 0;
			}

			/* finalize read.
			 * handle in TERMINATE and not DONE, since it may have skipped DONE by pre-reading*/
		  if(tm_is_read_req(req)){LOG("read req done\n");
				/* handle lba in different lists*/
		  	tm_terminate_lba_req(req);

		  	/* update any pedning reads so they can finish already*/
				tm_update_pending_lba_reads(req);
		  }

		  /* handle pending*/LOG("TM_TERMINATE. check if to dequeue pending_reclaim_req %x rec_chip_idx %x\n", specific->pending_reclaim_req, specific->rec_chip_idx);
			if(tm_is_with_pending_reclaim(req)){LOG("out_of_order req.\n");assert(!tm_is_reclaim_req(specific));
				/* write req done with current offset in rec_eu*/
				if(tm_is_simple_write_req(req)){LOG("out_of_order write req\n");
					/* nullify current of relevant chip*/
					tm.open_eu[specific->chip_idx].current = 0;
					/* make the pending_reclaim_req current again, in its relevant (rec_chip_idx!)*/
					tm.open_eu[TM_REQ_GET_SPECIFIC(specific->pending_reclaim_req)->rec_chip_idx].current = specific->pending_reclaim_req;
//					tm.rec_eu[specific->chip_idx].offset++;LOG("specific->curr_lba %u. reclaim request TM_DONE. delete list key\n", specific->curr_lba );
				}
//				/* read req done with lba. let reclaim req grab it
//				 * so it can continue straight off after finishing (otherwise other dequeued req may grab it)*/
//				else{LOG("out_of_order read req\n");assert(tm_is_read_req(req));
//					tm_acquire_lba(specific->curr_lba, dq_req);
//				}
			}
			/* finalize reclaim req*/
			else if(tm_is_reclaim_req(specific)){LOG("reclaim req done with current lba\n");assert(!tm_is_with_pending_reclaim(req));
				if(!tm_is_reclaim_terminated(req)){LOG("reclaim not done\n");
					TM_REQ_GET_SPECIFIC(req)->state = TM_RECLAIM_NEXT_LBA;
					req_activate(req,0);
					break;
				}
			}

			//			{
			//		  	uint32_t chip_idx;
			//		  	for(chip_idx=0; chip_idx< NAND_CHIP_COUNT;chip_idx++){
			//					uint32_t blk_idx_start = chip_idx * TM_BLOCKS_PER_CHIP;
			//					uint32_t last_blk_ix_in_chip = blk_idx_start + TM_BLOCKS_PER_CHIP;
			//					for(i=blk_idx_start; i<last_blk_ix_in_chip; i++){
			//						/*assert obs_count matches bits*/
			//						uint32_t j, count = 0;
			//						for(j=0;j<NAND_PAGES_PER_ERASE_UNIT;j++){
			//							count += !(tm_bitmap_get(tm.block[i].address+j));
			//						}
			//						if(tm.block[i].obs_count != count){
			//							LOG("mismatch in obs count. bitmap count %u tm.block[%u].obs_count %u\n", count, i, tm.block[i].obs_count);
			//							assert(tm.block[i].obs_count == count);
			//						}
			//					}
			//		  	}
			//			}
			LOG("activate caller\n");
			req_activate(req->caller_req, 0);
			LOG("check if to trigger reclamation\n");
			/* if we need to reclaim*/
			if(tm_req_triggered_reclaim(req)){
				baRequest(0, TM_REQ_GET_SPECIFIC(req)->chip_idx, BA_TYPE_TM);
			}

			/* done with everything. move on to deferred
			 * Are there callers who are waiting for request structures?*/
			if (tm.tmDeferred != 0) {
				pthread_mutex_lock(&(tm.defer_mutex));
				req->caller_req = tm.tmDeferred;
				tm.tmDeferred = tm.tmDeferred->next;
				tm.tmDeferred->prev = 0;
				req->next       = 0;
				pthread_mutex_unlock(&(tm.defer_mutex));

				req_activate(req->caller_req, req);
			} else {
				free(req->specific);
				free(req);
			}
			break;
		default:
			assert(0); // this should not happen
  }
}
