Use __blk_end_request() in zvol_request() to avoid ZVOL deadlock.

The unlocked version of blk_end_request() should be used in the
zvol_request() error handling to avoid deadlocking the request_queue
queue_lock.  Things get a little more complicated for older kernel
APIs but the compat layer has been updated as well to reflect this.
This commit is contained in:
Brian Behlendorf 2010-05-21 15:01:12 -07:00
parent cdd590f8b9
commit 94655e1c30
2 changed files with 37 additions and 19 deletions

View File

@ -61,9 +61,8 @@ blk_requeue_request(request_queue_t *q, struct request *req)
#ifndef HAVE_BLK_END_REQUEST
static inline bool
blk_end_request(struct request *req, int error, unsigned int nr_bytes)
__blk_end_request(struct request *req, int error, unsigned int nr_bytes)
{
struct request_queue *q = req->q;
LIST_HEAD(list);
/*
@ -79,14 +78,23 @@ blk_end_request(struct request *req, int error, unsigned int nr_bytes)
* entire request partial requests are not supported.
*/
req->hard_cur_sectors = nr_bytes >> 9;
spin_lock_irq(q->queue_lock);
end_request(req, ((error == 0) ? 1 : error));
spin_unlock_irq(q->queue_lock);
return 0;
}
static inline bool
blk_end_request(struct request *req, int error, unsigned int nr_bytes)
{
struct request_queue *q = req->q;
bool rc;
spin_lock_irq(q->queue_lock);
rc = __blk_end_request(req, error, nr_bytes);
spin_unlock_irq(q->queue_lock);
return rc;
}
#else
# ifdef HAVE_BLK_END_REQUEST_GPL_ONLY
/*
@ -94,25 +102,34 @@ blk_end_request(struct request *req, int error, unsigned int nr_bytes)
* GPL-only version of the helper. As of 2.6.31 the helper is available
* to non-GPL modules and is not explicitly exported GPL-only.
*/
# define blk_end_request ___blk_end_request
static inline bool
___blk_end_request(struct request *req, int error, unsigned int nr_bytes)
{
struct request_queue *q = req->q;
# define __blk_end_request __blk_end_request_x
# define blk_end_request blk_end_request_x
static inline bool
__blk_end_request_x(struct request *req, int error, unsigned int nr_bytes)
{
/*
* The old API required the driver to end each segment and not
* the entire request. In our case we always need to end the
* entire request partial requests are not supported.
*/
req->hard_cur_sectors = nr_bytes >> 9;
spin_lock_irq(q->queue_lock);
end_request(req, ((error == 0) ? 1 : error));
spin_unlock_irq(q->queue_lock);
return 0;
}
static inline bool
blk_end_request_x(struct request *req, int error, unsigned int nr_bytes)
{
struct request_queue *q = req->q;
bool rc;
spin_lock_irq(q->queue_lock);
__blk_end_request_x(req, error, nr_bytes);
spin_unlock_irq(q->queue_lock);
return rc;
}
# endif /* HAVE_BLK_END_REQUEST_GPL_ONLY */
#endif /* HAVE_BLK_END_REQUEST */

View File

@ -626,14 +626,14 @@ zvol_request(struct request_queue *q)
req->rq_disk->disk_name,
(long long unsigned)blk_rq_pos(req),
(long unsigned)blk_rq_sectors(req));
blk_end_request(req, -EIO, size);
__blk_end_request(req, -EIO, size);
continue;
}
if (!blk_fs_request(req)) {
printk(KERN_INFO "%s: non-fs cmd\n",
req->rq_disk->disk_name);
blk_end_request(req, -EIO, size);
__blk_end_request(req, -EIO, size);
continue;
}
@ -642,8 +642,9 @@ zvol_request(struct request_queue *q)
zvol_dispatch(zvol_read, req);
break;
case WRITE:
if (unlikely(get_disk_ro(zv->zv_disk))) {
blk_end_request(req, -EROFS, size);
if (unlikely(get_disk_ro(zv->zv_disk)) ||
unlikely(zv->zv_mode & DS_MODE_READONLY)) {
__blk_end_request(req, -EROFS, size);
break;
}
@ -652,7 +653,7 @@ zvol_request(struct request_queue *q)
default:
printk(KERN_INFO "%s: unknown cmd: %d\n",
req->rq_disk->disk_name, (int)rq_data_dir(req));
blk_end_request(req, -EIO, size);
__blk_end_request(req, -EIO, size);
break;
}
}