mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-25 13:43:55 +08:00
bfe159a512
USB surprise removal of sr is triggering an oops in scsi_dispatch_command(). What seems to be happening is that USB is hanging on to a queue reference until the last close of the upper device, so the crash is caused by surprise remove of a mounted CD followed by attempted unmount. The problem is that USB doesn't issue its final commands as part of the SCSI teardown path, but on last close when the block queue is long gone. The long term fix is probably to make sr do the teardown in the same way as sd (so remove all the lower bits on ejection, but keep the upper disk alive until last close of user space). However, the current oops can be simply fixed by not allowing any commands to be sent to a dead queue. Cc: stable@kernel.org Signed-off-by: James Bottomley <JBottomley@Parallels.com>
120 lines
3.0 KiB
C
120 lines
3.0 KiB
C
/*
|
|
* Functions related to setting various queue properties from drivers
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/bio.h>
|
|
#include <linux/blkdev.h>
|
|
|
|
#include "blk.h"
|
|
|
|
/*
|
|
* for max sense size
|
|
*/
|
|
#include <scsi/scsi_cmnd.h>
|
|
|
|
/**
|
|
* blk_end_sync_rq - executes a completion event on a request
|
|
* @rq: request to complete
|
|
* @error: end I/O status of the request
|
|
*/
|
|
static void blk_end_sync_rq(struct request *rq, int error)
|
|
{
|
|
struct completion *waiting = rq->end_io_data;
|
|
|
|
rq->end_io_data = NULL;
|
|
__blk_put_request(rq->q, rq);
|
|
|
|
/*
|
|
* complete last, if this is a stack request the process (and thus
|
|
* the rq pointer) could be invalid right after this complete()
|
|
*/
|
|
complete(waiting);
|
|
}
|
|
|
|
/**
|
|
* blk_execute_rq_nowait - insert a request into queue for execution
|
|
* @q: queue to insert the request in
|
|
* @bd_disk: matching gendisk
|
|
* @rq: request to insert
|
|
* @at_head: insert request at head or tail of queue
|
|
* @done: I/O completion handler
|
|
*
|
|
* Description:
|
|
* Insert a fully prepared request at the back of the I/O scheduler queue
|
|
* for execution. Don't wait for completion.
|
|
*/
|
|
void blk_execute_rq_nowait(struct request_queue *q, struct gendisk *bd_disk,
|
|
struct request *rq, int at_head,
|
|
rq_end_io_fn *done)
|
|
{
|
|
int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK;
|
|
|
|
if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) {
|
|
rq->errors = -ENXIO;
|
|
if (rq->end_io)
|
|
rq->end_io(rq, rq->errors);
|
|
return;
|
|
}
|
|
|
|
rq->rq_disk = bd_disk;
|
|
rq->end_io = done;
|
|
WARN_ON(irqs_disabled());
|
|
spin_lock_irq(q->queue_lock);
|
|
__elv_add_request(q, rq, where);
|
|
__blk_run_queue(q);
|
|
/* the queue is stopped so it won't be run */
|
|
if (rq->cmd_type == REQ_TYPE_PM_RESUME)
|
|
q->request_fn(q);
|
|
spin_unlock_irq(q->queue_lock);
|
|
}
|
|
EXPORT_SYMBOL_GPL(blk_execute_rq_nowait);
|
|
|
|
/**
|
|
* blk_execute_rq - insert a request into queue for execution
|
|
* @q: queue to insert the request in
|
|
* @bd_disk: matching gendisk
|
|
* @rq: request to insert
|
|
* @at_head: insert request at head or tail of queue
|
|
*
|
|
* Description:
|
|
* Insert a fully prepared request at the back of the I/O scheduler queue
|
|
* for execution and wait for completion.
|
|
*/
|
|
int blk_execute_rq(struct request_queue *q, struct gendisk *bd_disk,
|
|
struct request *rq, int at_head)
|
|
{
|
|
DECLARE_COMPLETION_ONSTACK(wait);
|
|
char sense[SCSI_SENSE_BUFFERSIZE];
|
|
int err = 0;
|
|
unsigned long hang_check;
|
|
|
|
/*
|
|
* we need an extra reference to the request, so we can look at
|
|
* it after io completion
|
|
*/
|
|
rq->ref_count++;
|
|
|
|
if (!rq->sense) {
|
|
memset(sense, 0, sizeof(sense));
|
|
rq->sense = sense;
|
|
rq->sense_len = 0;
|
|
}
|
|
|
|
rq->end_io_data = &wait;
|
|
blk_execute_rq_nowait(q, bd_disk, rq, at_head, blk_end_sync_rq);
|
|
|
|
/* Prevent hang_check timer from firing at us during very long I/O */
|
|
hang_check = sysctl_hung_task_timeout_secs;
|
|
if (hang_check)
|
|
while (!wait_for_completion_timeout(&wait, hang_check * (HZ/2)));
|
|
else
|
|
wait_for_completion(&wait);
|
|
|
|
if (rq->errors)
|
|
err = -EIO;
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(blk_execute_rq);
|