• Roland Dreier's avatar
    target: Fix race between multiple invocations of target_qf_do_work() · bcac364a
    Roland Dreier authored
    When work is scheduled with schedule_work(), the work can end up
    running on multiple CPUs at the same time -- this happens if
    the work is already running on one CPU and schedule_work() is called
    on another CPU.  This leads to list corruption with target_qf_do_work(),
    which is roughly doing:
    
    	spin_lock(...);
    	list_for_each_entry_safe(...) {
    		list_del(...);
    		spin_unlock(...);
    
    		// do stuff
    
    		spin_lock(...);
    	}
    
    With multiple CPUs running this code, one CPU can end up deleting the
    list entry that the other CPU is about to work on.
    
    Fix this by splicing the list entries onto a local list and then
    operating on that in the work function.  This way, each invocation of
    target_qf_do_work() operates on its own local list and so multiple
    invocations don't corrupt each other's list.  This also avoids dropping
    and reacquiring the lock for each list entry.
    Signed-off-by: default avatarRoland Dreier <roland@purestorage.com>
    Signed-off-by: default avatarNicholas Bellinger <nab@linux-iscsi.org>
    bcac364a
target_core_transport.c 143 KB