diff options
author | Tejun Heo <tj@kernel.org> | 2012-12-21 20:56:51 -0500 |
---|---|---|
committer | Anton Vorontsov <anton.vorontsov@linaro.org> | 2013-01-05 17:03:26 -0500 |
commit | 2fbb520d2079186727786b728ebc5bf20fc85520 (patch) | |
tree | c7367c9e969e7d1e38037b58217dc4c7a71536d5 /drivers | |
parent | 41468a11199212f092cb4c071626785db6a32393 (diff) |
charger_manager: Don't use [delayed_]work_pending()
There's no need to test whether a (delayed) work item in pending
before queueing, flushing or cancelling it. Most uses are unnecessary
and quite a few of them are buggy.
Remove unnecessary pending tests and rewrite _setup_polling() so that
it uses mod_delayed_work() if the next polling interval is sooner than
currently scheduled. queue_delayed_work() is used otherwise.
Only compile tested. I noticed that two work items - setup_polling
and cm_monitor_work - schedule each other. It's a very unusual
construct and I'm fairly sure it's racy. You can't break such
circular dependency by calling cancel on each. I strongly recommend
revising the mechanism.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Anton Vorontsov <cbou@mail.ru>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Donggeun Kim <dg77.kim@samsung.com>
Cc: MyungJoo Ham <myungjoo.ham@samsung.com>
Signed-off-by: Anton Vorontsov <anton.vorontsov@linaro.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/power/charger-manager.c | 31 |
1 files changed, 16 insertions, 15 deletions
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 029732d83090..8acc3f8d303c 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c | |||
@@ -669,15 +669,21 @@ static void _setup_polling(struct work_struct *work) | |||
669 | WARN(cm_wq == NULL, "charger-manager: workqueue not initialized" | 669 | WARN(cm_wq == NULL, "charger-manager: workqueue not initialized" |
670 | ". try it later. %s\n", __func__); | 670 | ". try it later. %s\n", __func__); |
671 | 671 | ||
672 | /* | ||
673 | * Use mod_delayed_work() iff the next polling interval should | ||
674 | * occur before the currently scheduled one. If @cm_monitor_work | ||
675 | * isn't active, the end result is the same, so no need to worry | ||
676 | * about stale @next_polling. | ||
677 | */ | ||
672 | _next_polling = jiffies + polling_jiffy; | 678 | _next_polling = jiffies + polling_jiffy; |
673 | 679 | ||
674 | if (!delayed_work_pending(&cm_monitor_work) || | 680 | if (time_before(_next_polling, next_polling)) { |
675 | (delayed_work_pending(&cm_monitor_work) && | ||
676 | time_after(next_polling, _next_polling))) { | ||
677 | next_polling = jiffies + polling_jiffy; | ||
678 | mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy); | 681 | mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy); |
682 | next_polling = _next_polling; | ||
683 | } else { | ||
684 | if (queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy)) | ||
685 | next_polling = _next_polling; | ||
679 | } | 686 | } |
680 | |||
681 | out: | 687 | out: |
682 | mutex_unlock(&cm_list_mtx); | 688 | mutex_unlock(&cm_list_mtx); |
683 | } | 689 | } |
@@ -751,8 +757,7 @@ static void misc_event_handler(struct charger_manager *cm, | |||
751 | if (cm_suspended) | 757 | if (cm_suspended) |
752 | device_set_wakeup_capable(cm->dev, true); | 758 | device_set_wakeup_capable(cm->dev, true); |
753 | 759 | ||
754 | if (!delayed_work_pending(&cm_monitor_work) && | 760 | if (is_polling_required(cm) && cm->desc->polling_interval_ms) |
755 | is_polling_required(cm) && cm->desc->polling_interval_ms) | ||
756 | schedule_work(&setup_polling); | 761 | schedule_work(&setup_polling); |
757 | uevent_notify(cm, default_event_names[type]); | 762 | uevent_notify(cm, default_event_names[type]); |
758 | } | 763 | } |
@@ -1170,8 +1175,7 @@ static int charger_extcon_notifier(struct notifier_block *self, | |||
1170 | * when charger cable is attached. | 1175 | * when charger cable is attached. |
1171 | */ | 1176 | */ |
1172 | if (cable->attached && is_polling_required(cable->cm)) { | 1177 | if (cable->attached && is_polling_required(cable->cm)) { |
1173 | if (work_pending(&setup_polling)) | 1178 | cancel_work_sync(&setup_polling); |
1174 | cancel_work_sync(&setup_polling); | ||
1175 | schedule_work(&setup_polling); | 1179 | schedule_work(&setup_polling); |
1176 | } | 1180 | } |
1177 | 1181 | ||
@@ -1718,10 +1722,8 @@ static int charger_manager_remove(struct platform_device *pdev) | |||
1718 | list_del(&cm->entry); | 1722 | list_del(&cm->entry); |
1719 | mutex_unlock(&cm_list_mtx); | 1723 | mutex_unlock(&cm_list_mtx); |
1720 | 1724 | ||
1721 | if (work_pending(&setup_polling)) | 1725 | cancel_work_sync(&setup_polling); |
1722 | cancel_work_sync(&setup_polling); | 1726 | cancel_delayed_work_sync(&cm_monitor_work); |
1723 | if (delayed_work_pending(&cm_monitor_work)) | ||
1724 | cancel_delayed_work_sync(&cm_monitor_work); | ||
1725 | 1727 | ||
1726 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { | 1728 | for (i = 0 ; i < desc->num_charger_regulators ; i++) { |
1727 | struct charger_regulator *charger | 1729 | struct charger_regulator *charger |
@@ -1790,8 +1792,7 @@ static int cm_suspend_prepare(struct device *dev) | |||
1790 | cm_suspended = true; | 1792 | cm_suspended = true; |
1791 | } | 1793 | } |
1792 | 1794 | ||
1793 | if (delayed_work_pending(&cm->fullbatt_vchk_work)) | 1795 | cancel_delayed_work(&cm->fullbatt_vchk_work); |
1794 | cancel_delayed_work(&cm->fullbatt_vchk_work); | ||
1795 | cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm); | 1796 | cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm); |
1796 | cm->status_save_batt = is_batt_present(cm); | 1797 | cm->status_save_batt = is_batt_present(cm); |
1797 | 1798 | ||