Do get_work in fill_queue without holding other locks.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
diff --git a/cgminer.c b/cgminer.c
index ad1ef95..42ee76d 100644
--- a/cgminer.c
+++ b/cgminer.c
@@ -6503,10 +6503,28 @@ static void hash_sole_work(struct thr_info *mythr)
static void fill_queue(struct thr_info *mythr, struct cgpu_info *cgpu, struct device_drv *drv, const int thr_id)
{
do {
- wr_lock(&cgpu->qlock);
- if (!cgpu->unqueued_work)
- cgpu->unqueued_work = get_work(mythr, thr_id);
- wr_unlock(&cgpu->qlock);
+ bool need_work;
+
+ /* Do this lockless just to know if we need more unqueued work. */
+ need_work = (!cgpu->unqueued_work);
+
+ /* get_work is a blocking function so do it outside of lock
+ * to prevent deadlocks with other locks. */
+ if (need_work) {
+ struct work *work = get_work(mythr, thr_id);
+
+ wr_lock(&cgpu->qlock);
+ /* Check we haven't grabbed work somehow between
+ * checking and picking up the lock. */
+ if (likely(!cgpu->unqueued_work))
+ cgpu->unqueued_work = work;
+ else
+ need_work = false;
+ wr_unlock(&cgpu->qlock);
+
+ if (unlikely(!need_work))
+ discard_work(work);
+ }
/* The queue_full function should be used by the driver to
* actually place work items on the physical device if it
* does have a queue. */