drivers/hwmon/occ: Switch to non-blocking for occ reads
PATCH 1/1 drivers/hwmon/occ: Switch to non-blocking for occ reads
From: Eddie James <[email protected]>
It's possible for the sbefifo ops to hang entirely if the system goes down (checkstop, etc), meaning that the occ and occ-hwmon drivers may hang during remove().
--- drivers/hwmon/occ/p9_sbe.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-)
drivers/hwmon/occ/p9_sbe.c +15 -4
@@ -16,7 +16,10 @@#include <linux/sched.h> #include <linux/workqueue.h> +#define OCC_CMD_INCOMPLETE_MS 10 +struct p9_sbe_occ { +bool cancel;struct occ occ;struct device *sbe; };
@@ -34,7 +37,7 @@ static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd) start = jiffies; retry: - client =occ_drv_open (p9_sbe_occ->sbe,0 ); + client =occ_drv_open (p9_sbe_occ->sbe,O_NONBLOCK );if (!client) { rc = -ENODEV ; goto assign;
@@ -44,9 +47,14 @@ static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd)if (rc <0 ) goto err; - rc =occ_drv_read (client, (char *)resp,sizeof (*resp)); -if (rc <0 ) - goto err; +do { + rc =occ_drv_read (client, (char *)resp,sizeof (*resp)); +if (rc != -EAGAIN ) +break ; + +set_current_state (TASK_INTERRUPTIBLE ); +schedule_timeout (msecs_to_jiffies (OCC_CMD_INCOMPLETE_MS )); + }while (!p9_sbe_occ->cancel);occ_drv_release (client);
@@ -164,6 +172,9 @@ static int p9_sbe_occ_probe(struct platform_device *pdev)static int p9_sbe_occ_remove (struct platform_device *pdev) {struct occ *occ =platform_get_drvdata (pdev); +struct p9_sbe_occ *p9_sbe_occ =to_p9_sbe_occ (occ); + + p9_sbe_occ->cancel = true;occ_remove_status_attrs (occ);
Signature · 1 line
1.8.3.1
Hi Eddie,
Quoted 3 lines
It's possible for the sbefifo ops to hang entirely if the system goes down (checkstop, etc), meaning that the occ and occ-hwmon drivers may hang during
remove().
How does this address that though? Your comment implies that a thread is stuck in occ_drv_read, is that correct?
I see that you're now opening in non-blocking mode, but:
drivers/hwmon/occ/p9_sbe.c static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd) · line 57
Quoted 11 lines
drivers/hwmon/occ/p9_sbe.c +8
-3- rc =occ_drv_read (client, (char *)resp,sizeof (*resp)); -if (rc <0 ) - goto err; +do { + rc =occ_drv_read (client, (char *)resp,sizeof (*resp)); +if (rc != -EAGAIN ) +break ; + +set_current_state (TASK_INTERRUPTIBLE ); +schedule_timeout (msecs_to_jiffies (OCC_CMD_INCOMPLETE_MS )); + }while (!p9_sbe_occ->cancel);
- the only way that you hit the !p9_sbe_occ->cancel condition is if we've run the ->remove callback, in which case we *definitely* shouldn't have threads still referencing the device. There seems to be nothing preventing p9_sbe_occ from being free()ed underneath you.
If this patch works, it suggests that there's either a reference-counting issue, or a memory leak.
Regards,
Jeremy
Quoted 7 lines · Jeremy Kerr
Hi Eddie,
Quoted 3 lines
It's possible for the sbefifo ops to hang entirely if the system goes down (checkstop, etc), meaning that the occ and occ-hwmon drivers may hang during
remove().How does this address that though? Your comment implies that a thread is stuck in
occ_drv_read, is that correct?
Correct.
Quoted 17 lines
I see that you're now opening in non-blocking mode, but:
drivers/hwmon/occ/p9_sbe.c static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd) · line 57
Quoted 11 lines
drivers/hwmon/occ/p9_sbe.c +8
-3- rc =occ_drv_read (client, (char *)resp,sizeof (*resp)); -if (rc <0 ) - goto err; +do { + rc =occ_drv_read (client, (char *)resp,sizeof (*resp)); +if (rc != -EAGAIN ) +break ; + +set_current_state (TASK_INTERRUPTIBLE ); +schedule_timeout (msecs_to_jiffies (OCC_CMD_INCOMPLETE_MS )); + }while (!p9_sbe_occ->cancel);- the only way that you hit the !p9_sbe_occ->cancel condition is if we've run the ->remove callback, in which case we *definitely* shouldn't have threads still referencing the device.
Hmm. In this error case, remove() is called from the sysfs unbind pathway. I could be wrong, but I don't see anything in the kernel to block in or return from unbind if there are hwmon files open. So I think it's possible to get to remove() with threads hanging around.
Quoted 2 lines
There seems to be nothing
preventingp9_sbe_occfrom being free()ed underneath you.
That is true... Most likely we get lucky due to trying to remove hwmon files before freeing p9_sbe_occ... I'll have to fix this.
Thanks for the review!
Eddie
Quoted 9 lines
If this patch works, it suggests that there's either a reference-counting issue, or a memory leak.
Regards,
Jeremy