Skip to content

Commit d79ff14

Browse files
Martin PeschkeJames Bottomley
Martin Peschke
authored and
James Bottomley
committed
[SCSI] zfcp: fix lock imbalance by reworking request queue locking
This patch adds wait_event_interruptible_lock_irq_timeout(), which is a straight-forward descendant of wait_event_interruptible_timeout() and wait_event_interruptible_lock_irq(). The zfcp driver used to call wait_event_interruptible_timeout() in combination with some intricate and error-prone locking. Using wait_event_interruptible_lock_irq_timeout() as a replacement nicely cleans up that locking. This rework removes a situation that resulted in a locking imbalance in zfcp_qdio_sbal_get(): BUG: workqueue leaked lock or atomic: events/1/0xffffff00/10 last function: zfcp_fc_wka_port_offline+0x0/0xa0 [zfcp] It was introduced by commit c2af754 "[SCSI] zfcp: Do not wait for SBALs on stopped queue", which had a new code path related to ZFCP_STATUS_ADAPTER_QDIOUP that took an early exit without a required lock being held. The problem occured when a special, non-SCSI I/O request was being submitted in process context, when the adapter's queues had been torn down. In this case the bug surfaced when the Fibre Channel port connection for a well-known address was closed during a concurrent adapter shut-down procedure, which is a rare constellation. This patch also fixes these warnings from the sparse tool (make C=1): drivers/s390/scsi/zfcp_qdio.c:224:12: warning: context imbalance in 'zfcp_qdio_sbal_check' - wrong count at exit drivers/s390/scsi/zfcp_qdio.c:244:5: warning: context imbalance in 'zfcp_qdio_sbal_get' - unexpected unlock Last but not least, we get rid of that crappy lock-unlock-lock sequence at the beginning of the critical section. It is okay to call zfcp_erp_adapter_reopen() with req_q_lock held. Reported-by: Mikulas Patocka <[email protected]> Reported-by: Heiko Carstens <[email protected]> Signed-off-by: Martin Peschke <[email protected]> Cc: [email protected] #2.6.35+ Signed-off-by: Steffen Maier <[email protected]> Signed-off-by: James Bottomley <[email protected]>
1 parent 35dc248 commit d79ff14

File tree

2 files changed

+59
-6
lines changed

2 files changed

+59
-6
lines changed

drivers/s390/scsi/zfcp_qdio.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,9 @@ int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req,
224224

225225
static int zfcp_qdio_sbal_check(struct zfcp_qdio *qdio)
226226
{
227-
spin_lock_irq(&qdio->req_q_lock);
228227
if (atomic_read(&qdio->req_q_free) ||
229228
!(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP))
230229
return 1;
231-
spin_unlock_irq(&qdio->req_q_lock);
232230
return 0;
233231
}
234232

@@ -246,9 +244,8 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio)
246244
{
247245
long ret;
248246

249-
spin_unlock_irq(&qdio->req_q_lock);
250-
ret = wait_event_interruptible_timeout(qdio->req_q_wq,
251-
zfcp_qdio_sbal_check(qdio), 5 * HZ);
247+
ret = wait_event_interruptible_lock_irq_timeout(qdio->req_q_wq,
248+
zfcp_qdio_sbal_check(qdio), qdio->req_q_lock, 5 * HZ);
252249

253250
if (!(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP))
254251
return -EIO;
@@ -262,7 +259,6 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio)
262259
zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdsbg_1");
263260
}
264261

265-
spin_lock_irq(&qdio->req_q_lock);
266262
return -EIO;
267263
}
268264

include/linux/wait.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,63 @@ do { \
811811
__ret; \
812812
})
813813

814+
#define __wait_event_interruptible_lock_irq_timeout(wq, condition, \
815+
lock, ret) \
816+
do { \
817+
DEFINE_WAIT(__wait); \
818+
\
819+
for (;;) { \
820+
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \
821+
if (condition) \
822+
break; \
823+
if (signal_pending(current)) { \
824+
ret = -ERESTARTSYS; \
825+
break; \
826+
} \
827+
spin_unlock_irq(&lock); \
828+
ret = schedule_timeout(ret); \
829+
spin_lock_irq(&lock); \
830+
if (!ret) \
831+
break; \
832+
} \
833+
finish_wait(&wq, &__wait); \
834+
} while (0)
835+
836+
/**
837+
* wait_event_interruptible_lock_irq_timeout - sleep until a condition gets true or a timeout elapses.
838+
* The condition is checked under the lock. This is expected
839+
* to be called with the lock taken.
840+
* @wq: the waitqueue to wait on
841+
* @condition: a C expression for the event to wait for
842+
* @lock: a locked spinlock_t, which will be released before schedule()
843+
* and reacquired afterwards.
844+
* @timeout: timeout, in jiffies
845+
*
846+
* The process is put to sleep (TASK_INTERRUPTIBLE) until the
847+
* @condition evaluates to true or signal is received. The @condition is
848+
* checked each time the waitqueue @wq is woken up.
849+
*
850+
* wake_up() has to be called after changing any variable that could
851+
* change the result of the wait condition.
852+
*
853+
* This is supposed to be called while holding the lock. The lock is
854+
* dropped before going to sleep and is reacquired afterwards.
855+
*
856+
* The function returns 0 if the @timeout elapsed, -ERESTARTSYS if it
857+
* was interrupted by a signal, and the remaining jiffies otherwise
858+
* if the condition evaluated to true before the timeout elapsed.
859+
*/
860+
#define wait_event_interruptible_lock_irq_timeout(wq, condition, lock, \
861+
timeout) \
862+
({ \
863+
int __ret = timeout; \
864+
\
865+
if (!(condition)) \
866+
__wait_event_interruptible_lock_irq_timeout( \
867+
wq, condition, lock, __ret); \
868+
__ret; \
869+
})
870+
814871

815872
/*
816873
* These are the old interfaces to sleep waiting for an event.

0 commit comments

Comments
 (0)