Skip to content

Commit

Permalink
nvme: stop aer posting if controller state not live
Browse files Browse the repository at this point in the history
If an nvme async_event command completes, in most cases, a new
async event is posted. However, if the controller enters a
resetting or reconnecting state, there is nothing to block the
scheduled work element from posting the async event again. Nor are
there calls from the transport to stop async events when an
association dies.

In the case of FC, where the association is torn down, the aer must
be aborted on the FC link and completes through the normal job
completion path. Thus the terminated async event ends up being
rescheduled even though the controller isn't in a valid state for
the aer, and the reposting gets the transport into a partially torn
down data structure.

It's possible to hit the scenario on rdma, although much less likely
due to an aer completing right as the association is terminated and
as the association teardown reclaims the blk requests via
nvme_cancel_request() so its immediate, not a link-related action
like on FC.

Fix by putting controller state checks in both the async event
completion routine where it schedules the async event and in the
async event work routine before it calls into the transport. It's
effectively a "stop_async_events()" behavior.  The transport, when
it creates a new association with the subsystem will transition
the state back to live and is already restarting the async event
posting.

Signed-off-by: James Smart <james.smart@broadcom.com>
[hch: remove taking a lock over reading the controller state]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
  • Loading branch information
James Smart authored and Jens Axboe committed Sep 25, 2017
1 parent d087747 commit cd48282
Showing 1 changed file with 3 additions and 2 deletions.
5 changes: 3 additions & 2 deletions drivers/nvme/host/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2590,7 +2590,7 @@ static void nvme_async_event_work(struct work_struct *work)
container_of(work, struct nvme_ctrl, async_event_work);

spin_lock_irq(&ctrl->lock);
while (ctrl->event_limit > 0) {
while (ctrl->state == NVME_CTRL_LIVE && ctrl->event_limit > 0) {
int aer_idx = --ctrl->event_limit;

spin_unlock_irq(&ctrl->lock);
Expand Down Expand Up @@ -2677,7 +2677,8 @@ void nvme_complete_async_event(struct nvme_ctrl *ctrl, __le16 status,
/*FALLTHRU*/
case NVME_SC_ABORT_REQ:
++ctrl->event_limit;
queue_work(nvme_wq, &ctrl->async_event_work);
if (ctrl->state == NVME_CTRL_LIVE)
schedule_work(&ctrl->async_event_work);
break;
default:
break;
Expand Down

0 comments on commit cd48282

Please sign in to comment.