Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

misc: mic: Fix crash when MIC reset is invoked in RESET_FAILED state

This patch fixes the following crash seen when MIC reset is invoked in
RESET_FAILED state due to device_del being called a second time on an
already deleted device:

[<ffffffff813b2295>] device_del+0x45/0x1d0
[<ffffffff813b243e>] device_unregister+0x1e/0x60
[<ffffffffa040f1c2>] scif_unregister_device+0x12/0x20 [scif_bus]
[<ffffffffa042f75a>] cosm_stop+0xaa/0xe0 [mic_cosm]
[<ffffffffa042f844>] cosm_reset_trigger_work+0x14/0x20 [mic_cosm]

The fix consists in realizing that because cosm_reset changes the
state to MIC_RESETTING, cosm_stop needs the previous state, before it
changed to MIC_RESETTING, to decide whether a hw_ops->stop had
previously been issued. This is now provided in a new cosm_device
member cdev->prev_state.

Reviewed-by: Sudeep Dutt <sudeep.dutt@intel.com>
Signed-off-by: Ashutosh Dixit <ashutosh.dixit@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Ashutosh Dixit and committed by
Greg Kroah-Hartman
f38e87e8 3f040887

+11 -4
+2
drivers/misc/mic/bus/cosm_bus.h
··· 30 30 * @attr_group: Pointer to list of sysfs attribute groups. 31 31 * @sdev: Device for sysfs entries. 32 32 * @state: MIC state. 33 + * @prev_state: MIC state previous to MIC_RESETTING 33 34 * @shutdown_status: MIC status reported by card for shutdown/crashes. 34 35 * @shutdown_status_int: Internal shutdown status maintained by the driver 35 36 * @cosm_mutex: Mutex for synchronizing access to data structures. ··· 56 55 const struct attribute_group **attr_group; 57 56 struct device *sdev; 58 57 u8 state; 58 + u8 prev_state; 59 59 u8 shutdown_status; 60 60 u8 shutdown_status_int; 61 61 struct mutex cosm_mutex;
+9 -4
drivers/misc/mic/cosm/cosm_main.c
··· 153 153 * stop(..) calls device_unregister and will crash the system if 154 154 * called multiple times. 155 155 */ 156 - bool call_hw_ops = cdev->state != MIC_RESET_FAILED && 157 - cdev->state != MIC_READY; 156 + u8 state = cdev->state == MIC_RESETTING ? 157 + cdev->prev_state : cdev->state; 158 + bool call_hw_ops = state != MIC_RESET_FAILED && 159 + state != MIC_READY; 158 160 159 161 if (cdev->state != MIC_RESETTING) 160 162 cosm_set_state(cdev, MIC_RESETTING); ··· 197 195 198 196 mutex_lock(&cdev->cosm_mutex); 199 197 if (cdev->state != MIC_READY) { 200 - cosm_set_state(cdev, MIC_RESETTING); 201 - schedule_work(&cdev->reset_trigger_work); 198 + if (cdev->state != MIC_RESETTING) { 199 + cdev->prev_state = cdev->state; 200 + cosm_set_state(cdev, MIC_RESETTING); 201 + schedule_work(&cdev->reset_trigger_work); 202 + } 202 203 } else { 203 204 dev_err(&cdev->dev, "%s %d MIC is READY\n", __func__, __LINE__); 204 205 rc = -EINVAL;