drivers: fsi: SBEFIFO: switch to workqueue instead of timer
PATCH 1/1 drivers: fsi: SBEFIFO: switch to workqueue instead of timer
From: "Edward A. James" <[email protected]>
Some systems are experiencing scheduling problems; mod_timer can take up to several seconds before the timer_function is executed, even if passed the current jiffies.
Work around this by using sbefifo's own workqueue.
--- drivers/fsi/fsi-sbefifo.c | 54 ++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 22 deletions(-)
drivers/fsi/fsi-sbefifo.c +32 -22
@@ -29,9 +29,9 @@#include <linux/sched.h> #include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/timer.h> #include <linux/uaccess.h> #include <linux/wait.h> +#include <linux/workqueue.h> /* * The SBEFIFO is a pipe-like FSI device for communicating with
@@ -57,7 +57,7 @@#define SBEFIFO_MAX_RESCHDULE msecs_to_jiffies(5000)struct sbefifo { -struct timer_list poll_timer; +struct delayed_work work;struct fsi_device *fsi_dev;struct miscdevice mdev;wait_queue_head_t wait;
@@ -99,6 +99,8 @@ struct sbefifo_client {unsigned long f_flags; }; +static struct workqueue_struct *sbefifo_wq; +static DEFINE_IDA (sbefifo_ida);static int sbefifo_inw (struct sbefifo *sbefifo,int reg,u32 *word)
@@ -337,7 +339,7 @@ static void sbefifo_client_release(struct kref *kref)*/ set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); } }
@@ -374,10 +376,11 @@ static struct sbefifo_xfr *sbefifo_next_xfr(struct sbefifo *sbefifo)return NULL ; } -static void sbefifo_poll_timer (unsigned long data) +static void sbefifo_worker (struct work_struct *work) {static const unsigned long EOT_MASK =0x000000ff ; -struct sbefifo *sbefifo = (void *)data; +struct delayed_work *dwork =to_delayed_work (work); + struct sbefifo *sbefifo = container_of(dwork, struct sbefifo, work);struct sbefifo_buf *rbuf, *wbuf;struct sbefifo_xfr *xfr, *tmp;struct sbefifo_buf drain;
@@ -393,6 +396,7 @@ static void sbefifo_poll_timer(unsigned long data)if (!xfr) goto out_unlock; +again: rbuf = xfr->rbuf; wbuf = xfr->wbuf;
@@ -414,9 +418,8 @@ static void sbefifo_poll_timer(unsigned long data) devn =sbefifo_dev_nwwriteable (sts);if (devn ==0 ) {/* No open slot for write. Reschedule. */ - sbefifo->poll_timer.expires = jiffies + -SBEFIFO_RESCHEDULE ; -add_timer (&sbefifo->poll_timer); +queue_delayed_work (sbefifo_wq, &sbefifo->work, +SBEFIFO_RESCHEDULE ); goto out_unlock; }
@@ -468,9 +471,8 @@ static void sbefifo_poll_timer(unsigned long data) }/* No data yet. Reschedule. */ - sbefifo->poll_timer.expires = jiffies + -SBEFIFO_RESCHEDULE ; -add_timer (&sbefifo->poll_timer); +queue_delayed_work (sbefifo_wq, &sbefifo->work, +SBEFIFO_RESCHEDULE ); goto out_unlock; }else { xfr->wait_data_timeout =0 ;
@@ -516,10 +518,12 @@ static void sbefifo_poll_timer(unsigned long data) }INIT_LIST_HEAD (&sbefifo->xfrs); - }else if (eot &&sbefifo_next_xfr (sbefifo)) { -sbefifo_get (sbefifo); - sbefifo->poll_timer.expires = jiffies; -add_timer (&sbefifo->poll_timer); + }else if (eot) { + xfr =sbefifo_next_xfr (sbefifo); +if (xfr) { +wake_up_interruptible (&sbefifo->wait); + goto again; + } }sbefifo_put (sbefifo);
@@ -638,7 +642,7 @@ static ssize_t sbefifo_read_common(struct sbefifo_client *client,* Fill the read buffer back up. */ sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); }else {list_del (&xfr->client);
@@ -721,7 +725,7 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, &n))) {set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&sbefifo->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); ret = -ERESTARTSYS ;
@@ -741,7 +745,8 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, n)) {set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&sbefifo->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, + &sbefifo->work.work))sbefifo_put (sbefifo); ret = -EFAULT ; goto out;
@@ -768,7 +773,7 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client,* Drain the write buffer. */ sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); }
@@ -928,8 +933,7 @@ static int sbefifo_probe(struct device *dev) sbefifo->idx);/* This bit of silicon doesn't offer any interrupts... */ -setup_timer (&sbefifo->poll_timer, sbefifo_poll_timer, - (unsigned long )sbefifo); +INIT_DELAYED_WORK (&sbefifo->work, sbefifo_worker); sbefifo->mdev.minor =MISC_DYNAMIC_MINOR ; sbefifo->mdev.fops = &sbefifo_fops;
@@ -982,7 +986,7 @@ static int sbefifo_remove(struct device *dev)ida_simple_remove (&sbefifo_ida, sbefifo->idx); -if (del_timer_sync (&sbefifo->poll_timer)) +if (cancel_delayed_work_sync (&sbefifo->work))sbefifo_put (sbefifo);sbefifo_put (sbefifo);
@@ -1010,11 +1014,17 @@ static int sbefifo_remove(struct device *dev)static int sbefifo_init (void ) { + sbefifo_wq =create_singlethread_workqueue ("sbefifo" ); +if (!sbefifo_wq) +return -ENOMEM ; +return fsi_driver_register (&sbefifo_drv); }static void sbefifo_exit (void ) { +destroy_workqueue (sbefifo_wq); +fsi_driver_unregister (&sbefifo_drv);ida_destroy (&sbefifo_ida);
Signature · 1 line
1.8.3.1
Quoted 5 lines · Eddie James
From: "Edward A. James" <[email protected]>
Some systems are experiencing scheduling problems;
mod_timercan take up to several seconds before thetimer_functionis executed, even if passed the current jiffies.
How did you work out that the timer wasn't firing?
Can you describe the other tests you have done?
It's not recommended to paper over something going wrong by changing over to a workqueue.
Cheers,
Joel
Quoted 190 lines
Work around this by using sbefifo's own workqueue.
Signed-off-by Edward A. James <[email protected]>--- drivers/fsi/fsi-sbefifo.c | 54 ++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 22 deletions(-)drivers/fsi/fsi-sbefifo.c +32
-23@@ -29,9 +29,9 @@#include <linux/sched.h> #include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/timer.h> #include <linux/uaccess.h> #include <linux/wait.h> +#include <linux/workqueue.h> /* * The SBEFIFO is a pipe-like FSI device for communicating with @@ -57,7 +57,7 @@#define SBEFIFO_MAX_RESCHDULE msecs_to_jiffies(5000)struct sbefifo { -struct timer_list poll_timer; +struct delayed_work work;struct fsi_device *fsi_dev;struct miscdevice mdev;wait_queue_head_t wait;@@ -99,6 +99,8 @@ struct sbefifo_client {unsigned long f_flags; }; +static struct workqueue_struct *sbefifo_wq; +static DEFINE_IDA (sbefifo_ida);static int sbefifo_inw (struct sbefifo *sbefifo,int reg,u32 *word)@@ -337,7 +339,7 @@ static void sbefifo_client_release(struct kref *kref)*/ set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); } }@@ -374,10 +376,11 @@ static struct sbefifo_xfr *sbefifo_next_xfr(struct sbefifo *sbefifo)return NULL ; } -static void sbefifo_poll_timer (unsigned long data) +static void sbefifo_worker (struct work_struct *work) {static const unsigned long EOT_MASK =0x000000ff ; -struct sbefifo *sbefifo = (void *)data; +struct delayed_work *dwork =to_delayed_work (work); + struct sbefifo *sbefifo = container_of(dwork, struct sbefifo, work);struct sbefifo_buf *rbuf, *wbuf;struct sbefifo_xfr *xfr, *tmp;struct sbefifo_buf drain;@@ -393,6 +396,7 @@ static void sbefifo_poll_timer(unsigned long data)if (!xfr) goto out_unlock; +again: rbuf = xfr->rbuf; wbuf = xfr->wbuf;@@ -414,9 +418,8 @@ static void sbefifo_poll_timer(unsigned long data) devn =sbefifo_dev_nwwriteable (sts);if (devn ==0 ) {/* No open slot for write. Reschedule. */ - sbefifo->poll_timer.expires = jiffies + -SBEFIFO_RESCHEDULE ; -add_timer (&sbefifo->poll_timer); +queue_delayed_work (sbefifo_wq, &sbefifo->work, +SBEFIFO_RESCHEDULE ); goto out_unlock; }@@ -468,9 +471,8 @@ static void sbefifo_poll_timer(unsigned long data) }/* No data yet. Reschedule. */ - sbefifo->poll_timer.expires = jiffies + -SBEFIFO_RESCHEDULE ; -add_timer (&sbefifo->poll_timer); +queue_delayed_work (sbefifo_wq, &sbefifo->work, +SBEFIFO_RESCHEDULE ); goto out_unlock; }else { xfr->wait_data_timeout =0 ;@@ -516,10 +518,12 @@ static void sbefifo_poll_timer(unsigned long data) }INIT_LIST_HEAD (&sbefifo->xfrs); - }else if (eot &&sbefifo_next_xfr (sbefifo)) { -sbefifo_get (sbefifo); - sbefifo->poll_timer.expires = jiffies; -add_timer (&sbefifo->poll_timer); + }else if (eot) { + xfr =sbefifo_next_xfr (sbefifo); +if (xfr) { +wake_up_interruptible (&sbefifo->wait); + goto again; + } }sbefifo_put (sbefifo);@@ -638,7 +642,7 @@ static ssize_t sbefifo_read_common(struct sbefifo_client *client,* Fill the read buffer back up. */ sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); }else {list_del (&xfr->client);@@ -721,7 +725,7 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, &n))) {set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&sbefifo->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); ret = -ERESTARTSYS ;@@ -741,7 +745,8 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, n)) {set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&sbefifo->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, + &sbefifo->work.work))sbefifo_put (sbefifo); ret = -EFAULT ; goto out;@@ -768,7 +773,7 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client,* Drain the write buffer. */ sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); }@@ -928,8 +933,7 @@ static int sbefifo_probe(struct device *dev) sbefifo->idx);/* This bit of silicon doesn't offer any interrupts... */ -setup_timer (&sbefifo->poll_timer, sbefifo_poll_timer, - (unsigned long )sbefifo); +INIT_DELAYED_WORK (&sbefifo->work, sbefifo_worker); sbefifo->mdev.minor =MISC_DYNAMIC_MINOR ; sbefifo->mdev.fops = &sbefifo_fops;@@ -982,7 +986,7 @@ static int sbefifo_remove(struct device *dev)ida_simple_remove (&sbefifo_ida, sbefifo->idx); -if (del_timer_sync (&sbefifo->poll_timer)) +if (cancel_delayed_work_sync (&sbefifo->work))sbefifo_put (sbefifo);sbefifo_put (sbefifo);@@ -1010,11 +1014,17 @@ static int sbefifo_remove(struct device *dev)static int sbefifo_init (void ) { + sbefifo_wq =create_singlethread_workqueue ("sbefifo" ); +if (!sbefifo_wq) +return -ENOMEM ; +return fsi_driver_register (&sbefifo_drv); }static void sbefifo_exit (void ) { +destroy_workqueue (sbefifo_wq); +fsi_driver_unregister (&sbefifo_drv);ida_destroy (&sbefifo_ida); --1.8.3.1
Quoted 7 lines · Joel Stanley
Quoted 5 lines · Eddie James
From: "Edward A. James" <[email protected]>
Some systems are experiencing scheduling problems;
mod_timercan take up to several seconds before thetimer_functionis executed, even if passed the current jiffies.
Quoted 6 lines
How did you work out that the timer wasn't firing?
Can you describe the other tests you have done?
It's not recommended to paper over something going wrong by changing over to a workqueue.
Nick pointed me to this fix, which was merged in 4.13:
2fe59f507a6 ("timers: Fix excessive granularity of new timers after a
nohz idle")
Can you cherry pick that into your tree and report the numbers that you see before and after the patch?
Quoted 191 lines
Quoted 191 lines
Work around this by using sbefifo's own workqueue.
Signed-off-by Edward A. James <[email protected]>--- drivers/fsi/fsi-sbefifo.c | 54 ++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 22 deletions(-)drivers/fsi/fsi-sbefifo.c +32
-23@@ -29,9 +29,9 @@#include <linux/sched.h> #include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/timer.h> #include <linux/uaccess.h> #include <linux/wait.h> +#include <linux/workqueue.h> /* * The SBEFIFO is a pipe-like FSI device for communicating with @@ -57,7 +57,7 @@#define SBEFIFO_MAX_RESCHDULE msecs_to_jiffies(5000)struct sbefifo { -struct timer_list poll_timer; +struct delayed_work work;struct fsi_device *fsi_dev;struct miscdevice mdev;wait_queue_head_t wait;@@ -99,6 +99,8 @@ struct sbefifo_client {unsigned long f_flags; }; +static struct workqueue_struct *sbefifo_wq; +static DEFINE_IDA (sbefifo_ida);static int sbefifo_inw (struct sbefifo *sbefifo,int reg,u32 *word)@@ -337,7 +339,7 @@ static void sbefifo_client_release(struct kref *kref)*/ set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); } }@@ -374,10 +376,11 @@ static struct sbefifo_xfr *sbefifo_next_xfr(struct sbefifo *sbefifo)return NULL ; } -static void sbefifo_poll_timer (unsigned long data) +static void sbefifo_worker (struct work_struct *work) {static const unsigned long EOT_MASK =0x000000ff ; -struct sbefifo *sbefifo = (void *)data; +struct delayed_work *dwork =to_delayed_work (work); + struct sbefifo *sbefifo = container_of(dwork, struct sbefifo, work);struct sbefifo_buf *rbuf, *wbuf;struct sbefifo_xfr *xfr, *tmp;struct sbefifo_buf drain;@@ -393,6 +396,7 @@ static void sbefifo_poll_timer(unsigned long data)if (!xfr) goto out_unlock; +again: rbuf = xfr->rbuf; wbuf = xfr->wbuf;@@ -414,9 +418,8 @@ static void sbefifo_poll_timer(unsigned long data) devn =sbefifo_dev_nwwriteable (sts);if (devn ==0 ) {/* No open slot for write. Reschedule. */ - sbefifo->poll_timer.expires = jiffies + -SBEFIFO_RESCHEDULE ; -add_timer (&sbefifo->poll_timer); +queue_delayed_work (sbefifo_wq, &sbefifo->work, +SBEFIFO_RESCHEDULE ); goto out_unlock; }@@ -468,9 +471,8 @@ static void sbefifo_poll_timer(unsigned long data) }/* No data yet. Reschedule. */ - sbefifo->poll_timer.expires = jiffies + -SBEFIFO_RESCHEDULE ; -add_timer (&sbefifo->poll_timer); +queue_delayed_work (sbefifo_wq, &sbefifo->work, +SBEFIFO_RESCHEDULE ); goto out_unlock; }else { xfr->wait_data_timeout =0 ;@@ -516,10 +518,12 @@ static void sbefifo_poll_timer(unsigned long data) }INIT_LIST_HEAD (&sbefifo->xfrs); - }else if (eot &&sbefifo_next_xfr (sbefifo)) { -sbefifo_get (sbefifo); - sbefifo->poll_timer.expires = jiffies; -add_timer (&sbefifo->poll_timer); + }else if (eot) { + xfr =sbefifo_next_xfr (sbefifo); +if (xfr) { +wake_up_interruptible (&sbefifo->wait); + goto again; + } }sbefifo_put (sbefifo);@@ -638,7 +642,7 @@ static ssize_t sbefifo_read_common(struct sbefifo_client *client,* Fill the read buffer back up. */ sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); }else {list_del (&xfr->client);@@ -721,7 +725,7 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, &n))) {set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&sbefifo->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); ret = -ERESTARTSYS ;@@ -741,7 +745,8 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, n)) {set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&sbefifo->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, + &sbefifo->work.work))sbefifo_put (sbefifo); ret = -EFAULT ; goto out;@@ -768,7 +773,7 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client,* Drain the write buffer. */ sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); }@@ -928,8 +933,7 @@ static int sbefifo_probe(struct device *dev) sbefifo->idx);/* This bit of silicon doesn't offer any interrupts... */ -setup_timer (&sbefifo->poll_timer, sbefifo_poll_timer, - (unsigned long )sbefifo); +INIT_DELAYED_WORK (&sbefifo->work, sbefifo_worker); sbefifo->mdev.minor =MISC_DYNAMIC_MINOR ; sbefifo->mdev.fops = &sbefifo_fops;@@ -982,7 +986,7 @@ static int sbefifo_remove(struct device *dev)ida_simple_remove (&sbefifo_ida, sbefifo->idx); -if (del_timer_sync (&sbefifo->poll_timer)) +if (cancel_delayed_work_sync (&sbefifo->work))sbefifo_put (sbefifo);sbefifo_put (sbefifo);@@ -1010,11 +1014,17 @@ static int sbefifo_remove(struct device *dev)static int sbefifo_init (void ) { + sbefifo_wq =create_singlethread_workqueue ("sbefifo" ); +if (!sbefifo_wq) +return -ENOMEM ; +return fsi_driver_register (&sbefifo_drv); }static void sbefifo_exit (void ) { +destroy_workqueue (sbefifo_wq); +fsi_driver_unregister (&sbefifo_drv);ida_destroy (&sbefifo_ida); --1.8.3.1
Quoted 13 lines · Joel Stanley
Quoted 6 lines · Joel Stanley
Quoted 5 lines · Eddie James
From: "Edward A. James" <[email protected]>
Some systems are experiencing scheduling problems;
mod_timercan take up to several seconds before thetimer_functionis executed, even if passed the current jiffies.Quoted 6 lines
How did you work out that the timer wasn't firing?
Can you describe the other tests you have done?
It's not recommended to paper over something going wrong by changing over to a workqueue.
Quoted 7 lines
Nick pointed me to this fix, which was merged in 4.13:
2fe59f507a6("timers: Fix excessive granularity of new timers after a
nohz idle")Can you cherry pick that into your tree and report the numbers that you see before and after the patch?
Just tried the fix out. I didn't see any difference unfortunately. I still get at least 250ms between calling mod_timer and the timer function starting. Just measuring with ftrace.
Thanks for the suggestion. It's possible the IPMI latency improved with the patch, but I don't know how to test that part.
Thanks,
Eddie
Quoted 190 lines
Quoted 190 lines
Quoted 190 lines
Work around this by using sbefifo's own workqueue.
Signed-off-by Edward A. James <[email protected]>--- drivers/fsi/fsi-sbefifo.c | 54 ++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 22 deletions(-)drivers/fsi/fsi-sbefifo.c +32
-23@@ -29,9 +29,9 @@#include <linux/sched.h> #include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/timer.h> #include <linux/uaccess.h> #include <linux/wait.h> +#include <linux/workqueue.h> /* * The SBEFIFO is a pipe-like FSI device for communicating with @@ -57,7 +57,7 @@#define SBEFIFO_MAX_RESCHDULE msecs_to_jiffies(5000)struct sbefifo { -struct timer_list poll_timer; +struct delayed_work work;struct fsi_device *fsi_dev;struct miscdevice mdev;wait_queue_head_t wait;@@ -99,6 +99,8 @@ struct sbefifo_client {unsigned long f_flags; }; +static struct workqueue_struct *sbefifo_wq; +static DEFINE_IDA (sbefifo_ida);static int sbefifo_inw (struct sbefifo *sbefifo,int reg,u32 *word)@@ -337,7 +339,7 @@ static void sbefifo_client_release(struct kref *kref)*/ set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); } }@@ -374,10 +376,11 @@ static struct sbefifo_xfr *sbefifo_next_xfr(struct sbefifo *sbefifo)return NULL ; } -static void sbefifo_poll_timer (unsigned long data) +static void sbefifo_worker (struct work_struct *work) {static const unsigned long EOT_MASK =0x000000ff ; -struct sbefifo *sbefifo = (void *)data; +struct delayed_work *dwork =to_delayed_work (work); + struct sbefifo *sbefifo = container_of(dwork, struct sbefifo, work);struct sbefifo_buf *rbuf, *wbuf;struct sbefifo_xfr *xfr, *tmp;struct sbefifo_buf drain;@@ -393,6 +396,7 @@ static void sbefifo_poll_timer(unsigned long data)if (!xfr) goto out_unlock; +again: rbuf = xfr->rbuf; wbuf = xfr->wbuf;@@ -414,9 +418,8 @@ static void sbefifo_poll_timer(unsigned long data) devn =sbefifo_dev_nwwriteable (sts);if (devn ==0 ) {/* No open slot for write. Reschedule. */ - sbefifo->poll_timer.expires = jiffies + -SBEFIFO_RESCHEDULE ; -add_timer (&sbefifo->poll_timer); +queue_delayed_work (sbefifo_wq, &sbefifo->work, +SBEFIFO_RESCHEDULE ); goto out_unlock; }@@ -468,9 +471,8 @@ static void sbefifo_poll_timer(unsigned long data) }/* No data yet. Reschedule. */ - sbefifo->poll_timer.expires = jiffies + -SBEFIFO_RESCHEDULE ; -add_timer (&sbefifo->poll_timer); +queue_delayed_work (sbefifo_wq, &sbefifo->work, +SBEFIFO_RESCHEDULE ); goto out_unlock; }else { xfr->wait_data_timeout =0 ;@@ -516,10 +518,12 @@ static void sbefifo_poll_timer(unsigned long data) }INIT_LIST_HEAD (&sbefifo->xfrs); - }else if (eot &&sbefifo_next_xfr (sbefifo)) { -sbefifo_get (sbefifo); - sbefifo->poll_timer.expires = jiffies; -add_timer (&sbefifo->poll_timer); + }else if (eot) { + xfr =sbefifo_next_xfr (sbefifo); +if (xfr) { +wake_up_interruptible (&sbefifo->wait); + goto again; + } }sbefifo_put (sbefifo);@@ -638,7 +642,7 @@ static ssize_t sbefifo_read_common(struct sbefifo_client *client,* Fill the read buffer back up. */ sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); }else {list_del (&xfr->client);@@ -721,7 +725,7 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, &n))) {set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&sbefifo->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); ret = -ERESTARTSYS ;@@ -741,7 +745,8 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, n)) {set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&sbefifo->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, + &sbefifo->work.work))sbefifo_put (sbefifo); ret = -EFAULT ; goto out;@@ -768,7 +773,7 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client,* Drain the write buffer. */ sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); }@@ -928,8 +933,7 @@ static int sbefifo_probe(struct device *dev) sbefifo->idx);/* This bit of silicon doesn't offer any interrupts... */ -setup_timer (&sbefifo->poll_timer, sbefifo_poll_timer, - (unsigned long )sbefifo); +INIT_DELAYED_WORK (&sbefifo->work, sbefifo_worker); sbefifo->mdev.minor =MISC_DYNAMIC_MINOR ; sbefifo->mdev.fops = &sbefifo_fops;@@ -982,7 +986,7 @@ static int sbefifo_remove(struct device *dev)ida_simple_remove (&sbefifo_ida, sbefifo->idx); -if (del_timer_sync (&sbefifo->poll_timer)) +if (cancel_delayed_work_sync (&sbefifo->work))sbefifo_put (sbefifo);sbefifo_put (sbefifo);@@ -1010,11 +1014,17 @@ static int sbefifo_remove(struct device *dev)static int sbefifo_init (void ) { + sbefifo_wq =create_singlethread_workqueue ("sbefifo" ); +if (!sbefifo_wq) +return -ENOMEM ; +return fsi_driver_register (&sbefifo_drv); }static void sbefifo_exit (void ) { +destroy_workqueue (sbefifo_wq); +fsi_driver_unregister (&sbefifo_drv);ida_destroy (&sbefifo_ida); --1.8.3.1
Quoted 7 lines · Joel Stanley
Quoted 5 lines · Eddie James
From: "Edward A. James" <[email protected]>
Some systems are experiencing scheduling problems;
mod_timercan take up to several seconds before thetimer_functionis executed, even if passed the current jiffies.How did you work out that the timer wasn't firing?
Just tracking the time when mod_timer is called and the time when the poll_timer function begins executing.
Quoted 2 lines
Can you describe the other tests you have done?
Did a bunch of OCC operations, some raw SCOM operations over SBE, repeated FSI scan/unscan, reboot the BMC while the host is up, and generated a checkstop on the host to see the SBEs stop working.
Quoted 3 lines
It's not recommended to paper over something going wrong by changing over to a workqueue.
Agreed... But not sure what else can be done. My commit message does say that this "works around" the issue.
Thanks!
Eddie
Quoted 194 lines
Cheers,
Joel
Quoted 189 lines
Work around this by using sbefifo's own workqueue.
Signed-off-by Edward A. James <[email protected]>--- drivers/fsi/fsi-sbefifo.c | 54 ++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 22 deletions(-)drivers/fsi/fsi-sbefifo.c +32
-23@@ -29,9 +29,9 @@#include <linux/sched.h> #include <linux/slab.h> #include <linux/spinlock.h> -#include <linux/timer.h> #include <linux/uaccess.h> #include <linux/wait.h> +#include <linux/workqueue.h> /* * The SBEFIFO is a pipe-like FSI device for communicating with @@ -57,7 +57,7 @@#define SBEFIFO_MAX_RESCHDULE msecs_to_jiffies(5000)struct sbefifo { -struct timer_list poll_timer; +struct delayed_work work;struct fsi_device *fsi_dev;struct miscdevice mdev;wait_queue_head_t wait;@@ -99,6 +99,8 @@ struct sbefifo_client {unsigned long f_flags; }; +static struct workqueue_struct *sbefifo_wq; +static DEFINE_IDA (sbefifo_ida);static int sbefifo_inw (struct sbefifo *sbefifo,int reg,u32 *word)@@ -337,7 +339,7 @@ static void sbefifo_client_release(struct kref *kref)*/ set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); } }@@ -374,10 +376,11 @@ static struct sbefifo_xfr *sbefifo_next_xfr(struct sbefifo *sbefifo)return NULL ; } -static void sbefifo_poll_timer (unsigned long data) +static void sbefifo_worker (struct work_struct *work) {static const unsigned long EOT_MASK =0x000000ff ; -struct sbefifo *sbefifo = (void *)data; +struct delayed_work *dwork =to_delayed_work (work); + struct sbefifo *sbefifo = container_of(dwork, struct sbefifo, work);struct sbefifo_buf *rbuf, *wbuf;struct sbefifo_xfr *xfr, *tmp;struct sbefifo_buf drain;@@ -393,6 +396,7 @@ static void sbefifo_poll_timer(unsigned long data)if (!xfr) goto out_unlock; +again: rbuf = xfr->rbuf; wbuf = xfr->wbuf;@@ -414,9 +418,8 @@ static void sbefifo_poll_timer(unsigned long data) devn =sbefifo_dev_nwwriteable (sts);if (devn ==0 ) {/* No open slot for write. Reschedule. */ - sbefifo->poll_timer.expires = jiffies + -SBEFIFO_RESCHEDULE ; -add_timer (&sbefifo->poll_timer); +queue_delayed_work (sbefifo_wq, &sbefifo->work, +SBEFIFO_RESCHEDULE ); goto out_unlock; }@@ -468,9 +471,8 @@ static void sbefifo_poll_timer(unsigned long data) }/* No data yet. Reschedule. */ - sbefifo->poll_timer.expires = jiffies + -SBEFIFO_RESCHEDULE ; -add_timer (&sbefifo->poll_timer); +queue_delayed_work (sbefifo_wq, &sbefifo->work, +SBEFIFO_RESCHEDULE ); goto out_unlock; }else { xfr->wait_data_timeout =0 ;@@ -516,10 +518,12 @@ static void sbefifo_poll_timer(unsigned long data) }INIT_LIST_HEAD (&sbefifo->xfrs); - }else if (eot &&sbefifo_next_xfr (sbefifo)) { -sbefifo_get (sbefifo); - sbefifo->poll_timer.expires = jiffies; -add_timer (&sbefifo->poll_timer); + }else if (eot) { + xfr =sbefifo_next_xfr (sbefifo); +if (xfr) { +wake_up_interruptible (&sbefifo->wait); + goto again; + } }sbefifo_put (sbefifo);@@ -638,7 +642,7 @@ static ssize_t sbefifo_read_common(struct sbefifo_client *client,* Fill the read buffer back up. */ sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); }else {list_del (&xfr->client);@@ -721,7 +725,7 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, &n))) {set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&sbefifo->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); ret = -ERESTARTSYS ;@@ -741,7 +745,8 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client, n)) {set_bit (SBEFIFO_XFR_CANCEL , &xfr->flags);sbefifo_get (sbefifo); -if (mod_timer (&sbefifo->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, + &sbefifo->work.work))sbefifo_put (sbefifo); ret = -EFAULT ; goto out;@@ -768,7 +773,7 @@ static ssize_t sbefifo_write_common(struct sbefifo_client *client,* Drain the write buffer. */ sbefifo_get (sbefifo); -if (mod_timer (&client->dev->poll_timer, jiffies)) +if (!queue_work (sbefifo_wq, &sbefifo->work.work))sbefifo_put (sbefifo); }@@ -928,8 +933,7 @@ static int sbefifo_probe(struct device *dev) sbefifo->idx);/* This bit of silicon doesn't offer any interrupts... */ -setup_timer (&sbefifo->poll_timer, sbefifo_poll_timer, - (unsigned long )sbefifo); +INIT_DELAYED_WORK (&sbefifo->work, sbefifo_worker); sbefifo->mdev.minor =MISC_DYNAMIC_MINOR ; sbefifo->mdev.fops = &sbefifo_fops;@@ -982,7 +986,7 @@ static int sbefifo_remove(struct device *dev)ida_simple_remove (&sbefifo_ida, sbefifo->idx); -if (del_timer_sync (&sbefifo->poll_timer)) +if (cancel_delayed_work_sync (&sbefifo->work))sbefifo_put (sbefifo);sbefifo_put (sbefifo);@@ -1010,11 +1014,17 @@ static int sbefifo_remove(struct device *dev)static int sbefifo_init (void ) { + sbefifo_wq =create_singlethread_workqueue ("sbefifo" ); +if (!sbefifo_wq) +return -ENOMEM ; +return fsi_driver_register (&sbefifo_drv); }static void sbefifo_exit (void ) { +destroy_workqueue (sbefifo_wq); +fsi_driver_unregister (&sbefifo_drv);ida_destroy (&sbefifo_ida); --1.8.3.1