File:  [Apple Darwin 0.x] / driverkit / notes / DMARaces
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:37:50 2018 UTC (8 years, 2 months ago) by root
Branches: MAIN, Apple
CVS tags: HEAD, Darwin03, Darwin02
Darwin 0.2 Driver Kit

			DMA enqueue race condition handling
		
hw_next_desc = nextDescriptor hw register
hw_curr_desc = currentDescriptor hw register
count_reg    = 'Count' hw register
frame_queue  = queue of kern_dma_frame_t (one per channel)
desc_queue   = queue of kern_dma_desc_t's (one per dma_frame_t)
last_frame   = end of frame_queue
last_desc    = end of last_frame's desc_queue

enqueue new_frame
{

	save current last_desc in old_tail;
	create chain for new_frame;
	link new_frame's chain onto old_tail;
	link new_frame onto frame_queue;
	if(hw_next_desc != NULL)
		done;
	else one of three cases:
		1. Missed update, channel still working on old_tail.
		2. Missed update, channel stopped.
		3. Update OK, channel working on new last_frame from 
		   (new_frame).
	if(hw_curr_desc != old_tail)
		return OK - case 3;
		
	/* 
	 * try to catch update in h/w.
	 */
	hw_next_desc = new_frame;	
	
	/*
	 * reading count register guarantees we avoid this race condition:
	 *
	 * -- TE finishes last desc, next_descriptor null, sends "clear 
	 *    descriptorListNotEmpty" packet to device.
	 * -- Kernel reads descriptorListNotEmpty, it's still 1 because the 
	 *    packet just sent to the device is NOT atomic and can be 
	 *    interrupted by a  kernel access (ONCE).
	 * Thus reading count does two things:
	 * -- guarantees that descriptorListNotEmpty will be valid 
	 *    (i.e., 0) when kernel reads it in case of underrun, and
	 * -- also tells kernel than channel is OK (running) if count is 
	 *    non-zero, since TE clears it before sending the "clear 
	 *    descriptorListNotEmpty" packet to the device.
	 */
	if((count != 0) || channel's descriptorListNotEmpty)
		return OK - case 1, did h/w update in time;
	if(hw_curr_desc == new last_desc)
		return OK - h/w update in time, and new_frame is already done
	
	/*
	 * case 3. Return error and prepare channel for restart.
	 */
	hw_curr_desc = new_frame;	
	return(missed error);
}	

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.