diff options
-rw-r--r-- | drivers/net/phy/phy.c | 3 | ||||
-rw-r--r-- | include/linux/workqueue.h | 1 | ||||
-rw-r--r-- | kernel/workqueue.c | 73 |
3 files changed, 75 insertions, 2 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 4044bb1ada86..e175f3910b18 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c | |||
@@ -587,8 +587,7 @@ int phy_stop_interrupts(struct phy_device *phydev) | |||
587 | * Finish any pending work; we might have been scheduled | 587 | * Finish any pending work; we might have been scheduled |
588 | * to be called from keventd ourselves, though. | 588 | * to be called from keventd ourselves, though. |
589 | */ | 589 | */ |
590 | if (!current_is_keventd()) | 590 | run_scheduled_work(&phydev->phy_queue); |
591 | flush_scheduled_work(); | ||
592 | 591 | ||
593 | free_irq(phydev->irq, phydev); | 592 | free_irq(phydev->irq, phydev); |
594 | 593 | ||
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index f0cb1df7b475..edef8d50b26b 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h | |||
@@ -162,6 +162,7 @@ extern int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, | |||
162 | extern void FASTCALL(flush_workqueue(struct workqueue_struct *wq)); | 162 | extern void FASTCALL(flush_workqueue(struct workqueue_struct *wq)); |
163 | 163 | ||
164 | extern int FASTCALL(schedule_work(struct work_struct *work)); | 164 | extern int FASTCALL(schedule_work(struct work_struct *work)); |
165 | extern int FASTCALL(run_scheduled_work(struct work_struct *work)); | ||
165 | extern int FASTCALL(schedule_delayed_work(struct delayed_work *work, unsigned long delay)); | 166 | extern int FASTCALL(schedule_delayed_work(struct delayed_work *work, unsigned long delay)); |
166 | 167 | ||
167 | extern int schedule_delayed_work_on(int cpu, struct delayed_work *work, unsigned long delay); | 168 | extern int schedule_delayed_work_on(int cpu, struct delayed_work *work, unsigned long delay); |
diff --git a/kernel/workqueue.c b/kernel/workqueue.c index c5257316f4b9..6b186750e9be 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c | |||
@@ -108,6 +108,79 @@ static inline void *get_wq_data(struct work_struct *work) | |||
108 | return (void *) (work->management & WORK_STRUCT_WQ_DATA_MASK); | 108 | return (void *) (work->management & WORK_STRUCT_WQ_DATA_MASK); |
109 | } | 109 | } |
110 | 110 | ||
111 | static int __run_work(struct cpu_workqueue_struct *cwq, struct work_struct *work) | ||
112 | { | ||
113 | int ret = 0; | ||
114 | unsigned long flags; | ||
115 | |||
116 | spin_lock_irqsave(&cwq->lock, flags); | ||
117 | /* | ||
118 | * We need to re-validate the work info after we've gotten | ||
119 | * the cpu_workqueue lock. We can run the work now iff: | ||
120 | * | ||
121 | * - the wq_data still matches the cpu_workqueue_struct | ||
122 | * - AND the work is still marked pending | ||
123 | * - AND the work is still on a list (which will be this | ||
124 | * workqueue_struct list) | ||
125 | * | ||
126 | * All these conditions are important, because we | ||
127 | * need to protect against the work being run right | ||
128 | * now on another CPU (all but the last one might be | ||
129 | * true if it's currently running and has not been | ||
130 | * released yet, for example). | ||
131 | */ | ||
132 | if (get_wq_data(work) == cwq | ||
133 | && work_pending(work) | ||
134 | && !list_empty(&work->entry)) { | ||
135 | work_func_t f = work->func; | ||
136 | list_del_init(&work->entry); | ||
137 | spin_unlock_irqrestore(&cwq->lock, flags); | ||
138 | |||
139 | if (!test_bit(WORK_STRUCT_NOAUTOREL, &work->management)) | ||
140 | work_release(work); | ||
141 | f(work); | ||
142 | |||
143 | spin_lock_irqsave(&cwq->lock, flags); | ||
144 | cwq->remove_sequence++; | ||
145 | wake_up(&cwq->work_done); | ||
146 | ret = 1; | ||
147 | } | ||
148 | spin_unlock_irqrestore(&cwq->lock, flags); | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * run_scheduled_work - run scheduled work synchronously | ||
154 | * @work: work to run | ||
155 | * | ||
156 | * This checks if the work was pending, and runs it | ||
157 | * synchronously if so. It returns a boolean to indicate | ||
158 | * whether it had any scheduled work to run or not. | ||
159 | * | ||
160 | * NOTE! This _only_ works for normal work_structs. You | ||
161 | * CANNOT use this for delayed work, because the wq data | ||
162 | * for delayed work will not point properly to the per- | ||
163 | * CPU workqueue struct, but will change! | ||
164 | */ | ||
165 | int fastcall run_scheduled_work(struct work_struct *work) | ||
166 | { | ||
167 | for (;;) { | ||
168 | struct cpu_workqueue_struct *cwq; | ||
169 | |||
170 | if (!work_pending(work)) | ||
171 | return 0; | ||
172 | if (list_empty(&work->entry)) | ||
173 | return 0; | ||
174 | /* NOTE! This depends intimately on __queue_work! */ | ||
175 | cwq = get_wq_data(work); | ||
176 | if (!cwq) | ||
177 | return 0; | ||
178 | if (__run_work(cwq, work)) | ||
179 | return 1; | ||
180 | } | ||
181 | } | ||
182 | EXPORT_SYMBOL(run_scheduled_work); | ||
183 | |||
111 | /* Preempt must be disabled. */ | 184 | /* Preempt must be disabled. */ |
112 | static void __queue_work(struct cpu_workqueue_struct *cwq, | 185 | static void __queue_work(struct cpu_workqueue_struct *cwq, |
113 | struct work_struct *work) | 186 | struct work_struct *work) |