aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBoaz Harrosh <bharrosh@panasas.com>2009-12-01 10:36:21 -0500
committerJames Bottomley <James.Bottomley@suse.de>2009-12-10 09:54:17 -0500
commit5d0961fd1f25e117f907f3af3aaa870637049252 (patch)
tree1eb877e773c511b3da68cab9da4ae0d0e0ee7369 /drivers
parentaeab3fd7b865bc4086a80a83cfdd67dded3b41a0 (diff)
[SCSI] libosd: Fix blk_put_request locking again
So libosd has decided to sacrifice some code simplicity for the sake of a clean API. One of these things is the possibility for users to call osd_end_request, in any condition at any state. This opens up some problems with calling blk_put_request when out-side of the completion callback but calling __blk_put_request when detecting a from-completion state. The current hack was working just fine until exofs decided to operate on all devices in parallel and wait for the sum of the requests, before deallocating all osd-requests at once. There are two new possible cases 1. All request in a group are deallocated as part of the last request's async-done, request_queue is locked. 2. All request in a group where executed asynchronously, but de-allocation was delayed to after the async-done, in the context of another thread. Async execution but request_queue is not locked. The solution I chose was to separate the deallocation of the osd_request which has the information users need, from the deallocation of the internal(2) requests which impose the locking problem. The internal block-requests are freed unconditionally inside the async-done-callback, when we know the queue is always locked. If at osd_end_request time we still have a bock-request, then we know it did not come from within an async-done-callback and we can call the regular blk_put_request. The internal requests were used for carrying error information after execution. This information is now copied to osd_request members for later analysis by user code. The external API and behaviour was unchanged, except now it really supports what was previously advertised. Reported-by: Vineet Agarwal <checkout.vineet@gmail.com> Signed-off-by: Boaz Harrosh <bharrosh@panasas.com> Cc: Stable Tree <stable@kernel.org> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/osd/osd_initiator.c88
1 files changed, 47 insertions, 41 deletions
diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c
index 950202a70bcf..24223473f573 100644
--- a/drivers/scsi/osd/osd_initiator.c
+++ b/drivers/scsi/osd/osd_initiator.c
@@ -432,30 +432,23 @@ static void _osd_free_seg(struct osd_request *or __unused,
432 seg->alloc_size = 0; 432 seg->alloc_size = 0;
433} 433}
434 434
435static void _put_request(struct request *rq , bool is_async) 435static void _put_request(struct request *rq)
436{ 436{
437 if (is_async) { 437 /*
438 WARN_ON(rq->bio); 438 * If osd_finalize_request() was called but the request was not
439 __blk_put_request(rq->q, rq); 439 * executed through the block layer, then we must release BIOs.
440 } else { 440 * TODO: Keep error code in or->async_error. Need to audit all
441 /* 441 * code paths.
442 * If osd_finalize_request() was called but the request was not 442 */
443 * executed through the block layer, then we must release BIOs. 443 if (unlikely(rq->bio))
444 * TODO: Keep error code in or->async_error. Need to audit all 444 blk_end_request(rq, -ENOMEM, blk_rq_bytes(rq));
445 * code paths. 445 else
446 */ 446 blk_put_request(rq);
447 if (unlikely(rq->bio))
448 blk_end_request(rq, -ENOMEM, blk_rq_bytes(rq));
449 else
450 blk_put_request(rq);
451 }
452} 447}
453 448
454void osd_end_request(struct osd_request *or) 449void osd_end_request(struct osd_request *or)
455{ 450{
456 struct request *rq = or->request; 451 struct request *rq = or->request;
457 /* IMPORTANT: make sure this agrees with osd_execute_request_async */
458 bool is_async = (or->request->end_io_data == or);
459 452
460 _osd_free_seg(or, &or->set_attr); 453 _osd_free_seg(or, &or->set_attr);
461 _osd_free_seg(or, &or->enc_get_attr); 454 _osd_free_seg(or, &or->enc_get_attr);
@@ -463,20 +456,34 @@ void osd_end_request(struct osd_request *or)
463 456
464 if (rq) { 457 if (rq) {
465 if (rq->next_rq) { 458 if (rq->next_rq) {
466 _put_request(rq->next_rq, is_async); 459 _put_request(rq->next_rq);
467 rq->next_rq = NULL; 460 rq->next_rq = NULL;
468 } 461 }
469 462
470 _put_request(rq, is_async); 463 _put_request(rq);
471 } 464 }
472 _osd_request_free(or); 465 _osd_request_free(or);
473} 466}
474EXPORT_SYMBOL(osd_end_request); 467EXPORT_SYMBOL(osd_end_request);
475 468
469static void _set_error_resid(struct osd_request *or, struct request *req,
470 int error)
471{
472 or->async_error = error;
473 or->req_errors = req->errors ? : error;
474 or->sense_len = req->sense_len;
475 if (or->out.req)
476 or->out.residual = or->out.req->resid_len;
477 if (or->in.req)
478 or->in.residual = or->in.req->resid_len;
479}
480
476int osd_execute_request(struct osd_request *or) 481int osd_execute_request(struct osd_request *or)
477{ 482{
478 return or->async_error = 483 int error = blk_execute_rq(or->request->q, NULL, or->request, 0);
479 blk_execute_rq(or->request->q, NULL, or->request, 0); 484
485 _set_error_resid(or, or->request, error);
486 return error;
480} 487}
481EXPORT_SYMBOL(osd_execute_request); 488EXPORT_SYMBOL(osd_execute_request);
482 489
@@ -484,15 +491,17 @@ static void osd_request_async_done(struct request *req, int error)
484{ 491{
485 struct osd_request *or = req->end_io_data; 492 struct osd_request *or = req->end_io_data;
486 493
487 or->async_error = error; 494 _set_error_resid(or, req, error);
488 495 if (req->next_rq) {
489 if (unlikely(error)) { 496 __blk_put_request(req->q, req->next_rq);
490 OSD_DEBUG("osd_request_async_done error recieved %d " 497 req->next_rq = NULL;
491 "errors 0x%x\n", error, req->errors);
492 if (!req->errors) /* don't miss out on this one */
493 req->errors = error;
494 } 498 }
495 499
500 __blk_put_request(req->q, req);
501 or->request = NULL;
502 or->in.req = NULL;
503 or->out.req = NULL;
504
496 if (or->async_done) 505 if (or->async_done)
497 or->async_done(or, or->async_private); 506 or->async_done(or, or->async_private);
498 else 507 else
@@ -1489,21 +1498,18 @@ int osd_req_decode_sense_full(struct osd_request *or,
1489#endif 1498#endif
1490 int ret; 1499 int ret;
1491 1500
1492 if (likely(!or->request->errors)) { 1501 if (likely(!or->req_errors))
1493 osi->out_resid = 0;
1494 osi->in_resid = 0;
1495 return 0; 1502 return 0;
1496 }
1497 1503
1498 osi = osi ? : &local_osi; 1504 osi = osi ? : &local_osi;
1499 memset(osi, 0, sizeof(*osi)); 1505 memset(osi, 0, sizeof(*osi));
1500 1506
1501 ssdb = or->request->sense; 1507 ssdb = (typeof(ssdb))or->sense;
1502 sense_len = or->request->sense_len; 1508 sense_len = or->sense_len;
1503 if ((sense_len < (int)sizeof(*ssdb) || !ssdb->sense_key)) { 1509 if ((sense_len < (int)sizeof(*ssdb) || !ssdb->sense_key)) {
1504 OSD_ERR("Block-layer returned error(0x%x) but " 1510 OSD_ERR("Block-layer returned error(0x%x) but "
1505 "sense_len(%u) || key(%d) is empty\n", 1511 "sense_len(%u) || key(%d) is empty\n",
1506 or->request->errors, sense_len, ssdb->sense_key); 1512 or->req_errors, sense_len, ssdb->sense_key);
1507 goto analyze; 1513 goto analyze;
1508 } 1514 }
1509 1515
@@ -1525,7 +1531,7 @@ int osd_req_decode_sense_full(struct osd_request *or,
1525 "additional_code=0x%x async_error=%d errors=0x%x\n", 1531 "additional_code=0x%x async_error=%d errors=0x%x\n",
1526 osi->key, original_sense_len, sense_len, 1532 osi->key, original_sense_len, sense_len,
1527 osi->additional_code, or->async_error, 1533 osi->additional_code, or->async_error,
1528 or->request->errors); 1534 or->req_errors);
1529 1535
1530 if (original_sense_len < sense_len) 1536 if (original_sense_len < sense_len)
1531 sense_len = original_sense_len; 1537 sense_len = original_sense_len;
@@ -1695,10 +1701,10 @@ analyze:
1695 ret = -EIO; 1701 ret = -EIO;
1696 } 1702 }
1697 1703
1698 if (or->out.req) 1704 if (!or->out.residual)
1699 osi->out_resid = or->out.req->resid_len ?: or->out.total_bytes; 1705 or->out.residual = or->out.total_bytes;
1700 if (or->in.req) 1706 if (!or->in.residual)
1701 osi->in_resid = or->in.req->resid_len ?: or->in.total_bytes; 1707 or->in.residual = or->in.total_bytes;
1702 1708
1703 return ret; 1709 return ret;
1704} 1710}