atl1c: Fix work event interrupt/task races

The mechanism used to initiate work events from the interrupt
handler has a classic read/modify/write race between the interrupt
handler that sets the condition, and the worker task that reads and
clears the condition. Close these races by using atomic
bit fields.

Cc: stable@kernel.org
Cc: Jie Yang <jie.yang@atheros.com>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by Tim Gardner and committed by David S. Miller cb771838 e74fbd03

+8 -12
+3 -3
drivers/net/atl1c/atl1c.h
··· 566 566 #define __AT_TESTING 0x0001 567 567 #define __AT_RESETTING 0x0002 568 568 #define __AT_DOWN 0x0003 569 - u8 work_event; 570 - #define ATL1C_WORK_EVENT_RESET 0x01 571 - #define ATL1C_WORK_EVENT_LINK_CHANGE 0x02 569 + unsigned long work_event; 570 + #define ATL1C_WORK_EVENT_RESET 0 571 + #define ATL1C_WORK_EVENT_LINK_CHANGE 1 572 572 u32 msg_enable; 573 573 574 574 bool have_msi;
+5 -9
drivers/net/atl1c/atl1c_main.c
··· 325 325 } 326 326 } 327 327 328 - adapter->work_event |= ATL1C_WORK_EVENT_LINK_CHANGE; 328 + set_bit(ATL1C_WORK_EVENT_LINK_CHANGE, &adapter->work_event); 329 329 schedule_work(&adapter->common_task); 330 330 } 331 331 ··· 337 337 adapter = container_of(work, struct atl1c_adapter, common_task); 338 338 netdev = adapter->netdev; 339 339 340 - if (adapter->work_event & ATL1C_WORK_EVENT_RESET) { 341 - adapter->work_event &= ~ATL1C_WORK_EVENT_RESET; 340 + if (test_and_clear_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event)) { 342 341 netif_device_detach(netdev); 343 342 atl1c_down(adapter); 344 343 atl1c_up(adapter); 345 344 netif_device_attach(netdev); 346 - return; 347 345 } 348 346 349 - if (adapter->work_event & ATL1C_WORK_EVENT_LINK_CHANGE) { 350 - adapter->work_event &= ~ATL1C_WORK_EVENT_LINK_CHANGE; 347 + if (test_and_clear_bit(ATL1C_WORK_EVENT_LINK_CHANGE, 348 + &adapter->work_event)) 351 349 atl1c_check_link_status(adapter); 352 - } 353 - return; 354 350 } 355 351 356 352 ··· 365 369 struct atl1c_adapter *adapter = netdev_priv(netdev); 366 370 367 371 /* Do the reset outside of interrupt context */ 368 - adapter->work_event |= ATL1C_WORK_EVENT_RESET; 372 + set_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event); 369 373 schedule_work(&adapter->common_task); 370 374 } 371 375