• David S. Miller's avatar
    brcmfmac: Use standard SKB list accessors in brcmf_sdiod_sglist_rw. · 4a5a553d
    David S. Miller authored
    Instead of direct SKB list pointer accesses.
    
    The loops in this function had to be rewritten to accommodate this
    more easily.
    
    The first loop iterates now over the target list in the outer loop,
    and triggers an mmc data operation when the per-operation limits are
    hit.
    
    Then after the loops, if we have any residue, we trigger the last
    and final operation.
    
    For the page aligned workaround, where we have to copy the read data
    back into the original list of SKBs, we use a two-tiered loop.  The
    outer loop stays the same and iterates over pktlist, and then we have
    an inner loop which uses skb_peek_next().  The break logic has been
    simplified because we know that the aggregate length of the SKBs in
    the source and destination lists are the same.
    
    This change also ends up fixing a bug, having to do with the
    maintainance of the seg_sz variable and how it drove the outermost
    loop.  It begins as:
    
    	seg_sz = target_list->qlen;
    
    ie. the number of packets in the target_list queue.  The loop
    structure was then:
    
    	while (seq_sz) {
    		...
    		while (not at end of target_list) {
    			...
    			sg_cnt++
    			...
    		}
    		...
    		seg_sz -= sg_cnt;
    
    The assumption built into that last statement is that sg_cnt counts
    how many packets from target_list have been fully processed by the
    inner loop.  But this not true.
    
    If we hit one of the limits, such as the max segment size or the max
    request size, we will break and copy a partial packet then contine
    back up to the top of the outermost loop.
    
    With the new loops we don't have this problem as we don't guard the
    loop exit with a packet count, but instead use the progression of the
    pkt_next SKB through the list to the end.  The general structure is:
    
    	sg_cnt = 0;
    	skb_queue_walk(target_list, pkt_next) {
    		pkt_offset = 0;
    		...
    		sg_cnt++;
    		...
    		while (pkt_offset < pkt_next->len) {
    			pkt_offset += sg_data_size;
    			if (queued up max per request)
    				mmc_submit_one();
    		}
    	}
    	if (sg_cnt)
    		mmc_submit_one();
    
    The variables that maintain where we are in the MMC command state such
    as req_sz, sg_cnt, and sgl are reset when we emit one of these full
    sized requests.
    Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
    4a5a553d
bcmsdh.c 30 KB