// File: /workspace/github_cpp_makefile_star2_O2/CMU-SAFARI[P]MQSim/src/ssd/Data_Cache_Manager_Flash_Advanced.cpp
// Function: SSD_Components::Data_Cache_Manager_Flash_Advanced::handle_transaction_serviced_signal_from_PHY(SSD_Components::NVM_Transaction_Flash*)
void Data_Cache_Manager_Flash_Advanced::handle_transaction_serviced_signal_from_PHY(NVM_Transaction_Flash* transaction)
	{
		//First check if the transaction source is a user request or the cache itself
		if (transaction->Source != Transaction_Source_Type::USERIO && transaction->Source != Transaction_Source_Type::CACHE) {
			return;
		}

		if (transaction->Source == Transaction_Source_Type::USERIO) {
			_my_instance->broadcast_user_memory_transaction_serviced_signal(transaction);
		}

		/* This is an update read (a read that is generated for a write request that partially updates page data).
		*  An update read transaction is issued in Address Mapping Unit, but is consumed in data cache manager.*/
		if (transaction->Type == Transaction_Type::READ) {
			if (((NVM_Transaction_Flash_RD*)transaction)->RelatedWrite != NULL) {
				((NVM_Transaction_Flash_RD*)transaction)->RelatedWrite->RelatedRead = NULL;
				return;
			}

			switch (Data_Cache_Manager_Flash_Advanced::caching_mode_per_input_stream[transaction->Stream_id])
			{
				case Caching_Mode::TURNED_OFF:
				case Caching_Mode::WRITE_CACHE:
					transaction->UserIORequest->Transaction_list.remove(transaction);
					if (_my_instance->is_user_request_finished(transaction->UserIORequest)) {
						_my_instance->broadcast_user_request_serviced_signal(transaction->UserIORequest);
					}
					break;
				case Caching_Mode::READ_CACHE:
				case Caching_Mode::WRITE_READ_CACHE:
				{					
					if (((Data_Cache_Manager_Flash_Advanced*)_my_instance)->per_stream_cache[transaction->Stream_id]->Exists(transaction->Stream_id, transaction->LPA)) {
						/*MQSim should get rid of writting stale data to the cache.
						* This situation may result from out-of-order transaction execution*/
						Data_Cache_Slot_Type slot = ((Data_Cache_Manager_Flash_Advanced*)_my_instance)->per_stream_cache[transaction->Stream_id]->Get_slot(transaction->Stream_id, transaction->LPA);
						sim_time_type timestamp = slot.Timestamp;
						NVM::memory_content_type content = slot.Content;
						if (((NVM_Transaction_Flash_RD*)transaction)->DataTimeStamp > timestamp) {
							timestamp = ((NVM_Transaction_Flash_RD*)transaction)->DataTimeStamp;
							content = ((NVM_Transaction_Flash_RD*)transaction)->Content;
						}

						((Data_Cache_Manager_Flash_Advanced*)_my_instance)->per_stream_cache[transaction->Stream_id]->Update_data(transaction->Stream_id, transaction->LPA, content,
							timestamp, ((NVM_Transaction_Flash_RD*)transaction)->read_sectors_bitmap | slot.State_bitmap_of_existing_sectors);
					} else  {
						if (!((Data_Cache_Manager_Flash_Advanced*)_my_instance)->per_stream_cache[transaction->Stream_id]->Check_free_slot_availability()) {
							std::list<NVM_Transaction*>* evicted_cache_slots = new std::list<NVM_Transaction*>;
							Data_Cache_Slot_Type evicted_slot = ((Data_Cache_Manager_Flash_Advanced*)_my_instance)->per_stream_cache[transaction->Stream_id]->Evict_one_slot_lru();
							if (evicted_slot.Status == Cache_Slot_Status::DIRTY_NO_FLASH_WRITEBACK) {
								Memory_Transfer_Info* transfer_info = new Memory_Transfer_Info;
								transfer_info->Size_in_bytes = count_sector_no_from_status_bitmap(evicted_slot.State_bitmap_of_existing_sectors) * SECTOR_SIZE_IN_BYTE;
								evicted_cache_slots->push_back(new NVM_Transaction_Flash_WR(Transaction_Source_Type::USERIO,
									transaction->Stream_id, transfer_info->Size_in_bytes, evicted_slot.LPA, NULL, IO_Flow_Priority_Class::UNDEFINED, evicted_slot.Content,
									evicted_slot.State_bitmap_of_existing_sectors, evicted_slot.Timestamp));
								transfer_info->Related_request = evicted_cache_slots;
								transfer_info->next_event_type = Data_Cache_Simulation_Event_Type::MEMORY_READ_FOR_CACHE_EVICTION_FINISHED;
								transfer_info->Stream_id = transaction->Stream_id;
								unsigned int cache_eviction_read_size_in_sectors = count_sector_no_from_status_bitmap(evicted_slot.State_bitmap_of_existing_sectors);
								int sharing_id = transaction->Stream_id;
								if (((Data_Cache_Manager_Flash_Advanced*)_my_instance)->shared_dram_request_queue) {
									sharing_id = 0;
								}
								((Data_Cache_Manager_Flash_Advanced*)_my_instance)->back_pressure_buffer_depth[sharing_id] += cache_eviction_read_size_in_sectors;
								((Data_Cache_Manager_Flash_Advanced*)_my_instance)->service_dram_access_request(transfer_info);
							}
						}
						((Data_Cache_Manager_Flash_Advanced*)_my_instance)->per_stream_cache[transaction->Stream_id]->Insert_read_data(transaction->Stream_id, transaction->LPA,
							((NVM_Transaction_Flash_RD*)transaction)->Content, ((NVM_Transaction_Flash_RD*)transaction)->DataTimeStamp, ((NVM_Transaction_Flash_RD*)transaction)->read_sectors_bitmap);

						Memory_Transfer_Info* transfer_info = new Memory_Transfer_Info;
						transfer_info->Size_in_bytes = count_sector_no_from_status_bitmap(((NVM_Transaction_Flash_RD*)transaction)->read_sectors_bitmap) * SECTOR_SIZE_IN_BYTE;
						transfer_info->next_event_type = Data_Cache_Simulation_Event_Type::MEMORY_WRITE_FOR_CACHE_FINISHED;
						transfer_info->Stream_id = transaction->Stream_id;
						((Data_Cache_Manager_Flash_Advanced*)_my_instance)->service_dram_access_request(transfer_info);
					}

					transaction->UserIORequest->Transaction_list.remove(transaction);
					if (_my_instance->is_user_request_finished(transaction->UserIORequest)) {
						_my_instance->broadcast_user_request_serviced_signal(transaction->UserIORequest);
					}
					break;
				}
			}
		} else {//This is a write request
			switch (Data_Cache_Manager_Flash_Advanced::caching_mode_per_input_stream[transaction->Stream_id])
			{
				case Caching_Mode::TURNED_OFF:
				case Caching_Mode::READ_CACHE:
					transaction->UserIORequest->Transaction_list.remove(transaction);
					if (_my_instance->is_user_request_finished(transaction->UserIORequest)) {
						_my_instance->broadcast_user_request_serviced_signal(transaction->UserIORequest);
					}
					break;
				case Caching_Mode::WRITE_CACHE:
				case Caching_Mode::WRITE_READ_CACHE:
				{
					int sharing_id = transaction->Stream_id;
					if (((Data_Cache_Manager_Flash_Advanced*)_my_instance)->shared_dram_request_queue) {
						sharing_id = 0;
					}
					((Data_Cache_Manager_Flash_Advanced*)_my_instance)->back_pressure_buffer_depth[sharing_id] -= transaction->Data_and_metadata_size_in_byte / SECTOR_SIZE_IN_BYTE + (transaction->Data_and_metadata_size_in_byte % SECTOR_SIZE_IN_BYTE == 0 ? 0 : 1);

					if (((Data_Cache_Manager_Flash_Advanced*)_my_instance)->per_stream_cache[transaction->Stream_id]->Exists(transaction->Stream_id, ((NVM_Transaction_Flash_WR*)transaction)->LPA)) {
						Data_Cache_Slot_Type slot = ((Data_Cache_Manager_Flash_Advanced*)_my_instance)->per_stream_cache[transaction->Stream_id]->Get_slot(transaction->Stream_id, ((NVM_Transaction_Flash_WR*)transaction)->LPA);
						sim_time_type timestamp = slot.Timestamp;
						NVM::memory_content_type content = slot.Content;
						if (((NVM_Transaction_Flash_WR*)transaction)->DataTimeStamp >= timestamp) {
							((Data_Cache_Manager_Flash_Advanced*)_my_instance)->per_stream_cache[transaction->Stream_id]->Remove_slot(transaction->Stream_id, ((NVM_Transaction_Flash_WR*)transaction)->LPA);
						}
					}
					
					auto user_request = ((Data_Cache_Manager_Flash_Advanced*)_my_instance)->waiting_user_requests_queue_for_dram_free_slot[sharing_id].begin();
					while (user_request != ((Data_Cache_Manager_Flash_Advanced*)_my_instance)->waiting_user_requests_queue_for_dram_free_slot[sharing_id].end())
					{
						((Data_Cache_Manager_Flash_Advanced*)_my_instance)->write_to_destage_buffer(*user_request);
						if ((*user_request)->Transaction_list.size() == 0) {
							((Data_Cache_Manager_Flash_Advanced*)_my_instance)->waiting_user_requests_queue_for_dram_free_slot[sharing_id].erase(user_request++);
						} else {
							user_request++;
						}
						//The traffic load on the backend is high and the waiting requests cannot be serviced
						if (((Data_Cache_Manager_Flash_Advanced*)_my_instance)->back_pressure_buffer_depth[sharing_id] > ((Data_Cache_Manager_Flash_Advanced*)_my_instance)->back_pressure_buffer_max_depth) {
							break;
						}
					}

					/*if (_my_instance->back_pressure_buffer_depth[sharing_id] < _my_instance->back_pressure_buffer_max_depth)//The traffic load on the backend is low and the waiting requests can be serviced
					{
						std::list<NVM_Transaction*>* evicted_cache_slots = new std::list<NVM_Transaction*>;
						while (!((Data_Cache_Manager_Flash_Advanced*)_my_instance)->per_stream_cache[transaction->Stream_id]->Empty())
						{
							DataCacheSlotType evicted_slot = ((Data_Cache_Manager_Flash_Advanced*)_my_instance)->per_stream_cache[transaction->Stream_id]->Evict_one_dirty_slot();
							if (evicted_slot.Status != CacheSlotStatus::EMPTY)
							{
								evicted_cache_slots->push_back(new NVM_Transaction_Flash_WR(Transaction_Source_Type::CACHE,
									transaction->Stream_id, count_sector_no_from_status_bitmap(evicted_slot.State_bitmap_of_existing_sectors) * SECTOR_SIZE_IN_BYTE,
									evicted_slot.LPA, NULL, IO_Flow_Priority_Class::UNDEFINED, evicted_slot.Content, evicted_slot.State_bitmap_of_existing_sectors, evicted_slot.Timestamp));
								_my_instance->back_pressure_buffer_depth[sharing_id] += count_sector_no_from_status_bitmap(evicted_slot.State_bitmap_of_existing_sectors);
								cache_eviction_read_size_in_sectors += count_sector_no_from_status_bitmap(evicted_slot.State_bitmap_of_existing_sectors);
							}
							else break;
							if (_my_instance->back_pressure_buffer_depth[sharing_id] >= _my_instance->back_pressure_buffer_max_depth)
								break;
						}
						
						if (cache_eviction_read_size_in_sectors > 0)
						{
							Memory_Transfer_Info* read_transfer_info = new Memory_Transfer_Info;
							read_transfer_info->Size_in_bytes = cache_eviction_read_size_in_sectors * SECTOR_SIZE_IN_BYTE;
							read_transfer_info->Related_request = evicted_cache_slots;
							read_transfer_info->next_event_type = Data_Cache_Simulation_Event_Type::MEMORY_READ_FOR_CACHE_EVICTION_FINISHED;
							read_transfer_info->Stream_id = transaction->Stream_id;
							((Data_Cache_Manager_Flash_Advanced*)_my_instance)->service_dram_access_request(read_transfer_info);
						}
					}*/

					break;
				}
			}
		}
	}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x38, %rsp
movl 0x4(%rdi), %eax
cmpl $0x1, %eax
ja 0x2a28d
movq %rdi, %rbx
testl %eax, %eax
jne 0x29ea3
leaq 0x38758(%rip), %rax     # 0x625f0
movq (%rax), %rdi
movq %rbx, %rsi
callq 0x295b0
cmpl $0x0, 0x8(%rbx)
je 0x29fe9
leaq 0x38744(%rip), %rax     # 0x625f8
movq (%rax), %rax
movzwl (%rbx), %esi
movl (%rax,%rsi,4), %eax
cmpq $0x3, %rax
ja 0x2a28d
leaq 0x1d19a(%rip), %rcx     # 0x47068
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
leaq 0x38712(%rip), %r15     # 0x625f0
movq (%r15), %rax
movl 0x50(%rbx), %ecx
movl %ecx, %edx
shrl $0x9, %edx
andl $0x1ff, %ecx            # imm = 0x1FF
xorl %r12d, %r12d
cmpb $0x0, 0xe0(%rax)
movq 0xf0(%rax), %rdi
cmoveq %rsi, %r12
movl (%rdi,%r12,4), %r8d
subl %edx, %r8d
cmpl $0x1, %ecx
adcl $-0x1, %r8d
movl %r8d, (%rdi,%r12,4)
movq 0xc0(%rax), %rax
movq (%rax,%rsi,8), %rdi
movq 0x58(%rbx), %rdx
callq 0x287ce
testb %al, %al
je 0x29f75
movq (%r15), %rax
movq 0xc0(%rax), %rax
movzwl (%rbx), %edx
movq (%rax,%rdx,8), %rsi
movq 0x58(%rbx), %rcx
movq %rsp, %r14
movq %r14, %rdi
callq 0x28822
movq 0xa0(%rbx), %rax
cmpq 0x18(%r14), %rax
jb 0x29f75
movq (%r15), %rax
movq 0xc0(%rax), %rax
movzwl (%rbx), %esi
movq (%rax,%rsi,8), %rdi
movq 0x58(%rbx), %rdx
callq 0x28d10
movq (%r15), %rdi
movq 0xd8(%rdi), %rax
imulq $0x18, %r12, %r14
movq (%rax,%r14), %rbx
movq 0xd8(%rdi), %rax
addq %r14, %rax
cmpq %rax, %rbx
je 0x2a28d
movq 0x10(%rbx), %rsi
callq 0x2a554
movq 0x10(%rbx), %rax
cmpq $0x0, 0x50(%rax)
je 0x29fb3
movq (%rbx), %rbx
jmp 0x29fce
movq (%r15), %rax
movq 0xd8(%rax), %rdi
addq %r14, %rdi
movq (%rbx), %r13
movq %rbx, %rsi
callq 0x2abaa
movq %r13, %rbx
movq (%r15), %rdi
movq 0xf0(%rdi), %rax
movl (%rax,%r12,4), %eax
cmpl 0xe8(%rdi), %eax
jbe 0x29f87
jmp 0x2a28d
movq 0x88(%rbx), %rax
testq %rax, %rax
je 0x2a002
andq $0x0, 0x88(%rax)
jmp 0x2a28d
leaq 0x385ef(%rip), %rax     # 0x625f8
movq (%rax), %rax
movzwl (%rbx), %esi
movl (%rax,%rsi,4), %eax
leal -0x1(%rax), %ecx
cmpl $0x2, %ecx
jb 0x2a067
cmpl $0x3, %eax
je 0x2a027
testl %eax, %eax
jne 0x2a28d
movq 0x10(%rbx), %rdi
addq $0x40, %rdi
movq %rsp, %rsi
movq %rbx, (%rsi)
callq 0x2ab4c
movq 0x10(%rbx), %rsi
cmpq $0x0, 0x50(%rsi)
jne 0x2a28d
cmpl $0x0, 0x58(%rsi)
jne 0x2a28d
leaq 0x38596(%rip), %rax     # 0x625f0
movq (%rax), %rdi
callq 0x2956e
jmp 0x2a28d
leaq 0x38582(%rip), %r13     # 0x625f0
movq (%r13), %rax
movq 0xc0(%rax), %rax
movq (%rax,%rsi,8), %rdi
movq 0x58(%rbx), %rdx
callq 0x287ce
testb %al, %al
je 0x2a0ce
movq (%r13), %rax
movq 0xc0(%rax), %rax
movzwl (%rbx), %edx
movq (%rax,%rdx,8), %rsi
movq 0x58(%rbx), %rcx
movq %rsp, %r14
movq %r14, %rdi
callq 0x28822
movq 0x18(%r14), %r8
movq 0x98(%rbx), %rax
cmpq %r8, %rax
jbe 0x2a23e
movq 0x80(%rbx), %rcx
movq %rax, %r8
jmp 0x2a243
movq (%r13), %rax
movq 0xc0(%rax), %rax
movzwl (%rbx), %ecx
movq (%rax,%rcx,8), %rdi
callq 0x288ba
testb %al, %al
jne 0x2a1d3
pushq $0x18
popq %rdi
callq 0x7370
movq %rax, %r14
movq %rax, 0x8(%rax)
movq %rax, (%rax)
andq $0x0, 0x10(%rax)
movq (%r13), %rax
movq 0xc0(%rax), %rax
movzwl (%rbx), %ecx
movq (%rax,%rcx,8), %rsi
movq %rsp, %r15
movq %r15, %rdi
callq 0x28994
cmpl $0x2, 0x20(%r15)
jne 0x2a1d3
pushq $0x18
popq %rdi
callq 0x7370
movq %rax, %r15
movq (%rsp), %rdi
callq 0x2661d
movl %eax, %ebp
shll $0x9, %ebp
movl %ebp, (%r15)
movl $0xb0, %edi
callq 0x7370
movq %rax, %r12
movzwl (%rbx), %edx
movq 0x8(%rsp), %r8
movq %rax, %rdi
xorl %esi, %esi
movl %ebp, %ecx
xorl %r9d, %r9d
pushq 0x18(%rsp)
pushq 0x8(%rsp)
pushq 0x20(%rsp)
pushq $0x2710                 # imm = 0x2710
callq 0x3a310
addq $0x20, %rsp
leaq 0x30(%rsp), %rsi
movq %r12, (%rsi)
movq %r14, %rdi
callq 0x2ab40
movq %r14, 0x8(%r15)
andl $0x0, 0x10(%r15)
movzwl (%rbx), %eax
movw %ax, 0x14(%r15)
movq (%rsp), %rdi
callq 0x2661d
movzwl (%rbx), %ecx
movq (%r13), %rdi
cmpb $0x0, 0xe0(%rdi)
movl $0x0, %edx
cmovnel %edx, %ecx
movq 0xf0(%rdi), %rdx
addl %eax, (%rdx,%rcx,4)
movq %r15, %rsi
callq 0x2a4a2
movq (%r13), %rax
movq 0xc0(%rax), %rax
movzwl (%rbx), %esi
movq (%rax,%rsi,8), %rdi
movq 0x58(%rbx), %rdx
movq 0x80(%rbx), %rcx
movq 0x98(%rbx), %r8
movq 0x90(%rbx), %r9
callq 0x28a52
pushq $0x18
popq %rdi
callq 0x7370
movq %rax, %r14
movq 0x90(%rbx), %rdi
callq 0x2661d
shll $0x9, %eax
movl %eax, (%r14)
movl $0x1, 0x10(%r14)
movzwl (%rbx), %eax
movw %ax, 0x14(%r14)
movq (%r13), %rdi
movq %r14, %rsi
callq 0x2a4a2
jmp 0x2a269
movq 0x10(%rsp), %rcx
movq (%r13), %rax
movq 0xc0(%rax), %rax
movzwl (%rbx), %esi
movq (%rax,%rsi,8), %rdi
movq 0x58(%rbx), %rdx
movq (%rsp), %r9
orq 0x90(%rbx), %r9
callq 0x28c6e
movq 0x10(%rbx), %rdi
addq $0x40, %rdi
movq %rsp, %rsi
movq %rbx, (%rsi)
callq 0x2ab4c
movq 0x10(%rbx), %rsi
cmpq $0x0, 0x50(%rsi)
jne 0x2a28d
cmpl $0x0, 0x58(%rsi)
je 0x2a29c
addq $0x38, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq (%r13), %rdi
jmp 0x2a05d
movq %rax, %rbx
movq %r12, %rdi
callq 0x7340
movq %rbx, %rdi
callq 0x75d0