| From 7512a1fac837476d4bf5d637ecf6044f180784e0 Mon Sep 17 00:00:00 2001 |
| From: "David S. Miller" <davem@davemloft.net> |
| Date: Thu, 1 Aug 2013 18:08:34 -0700 |
| Subject: esp_scsi: Fix tag state corruption when autosensing. |
| |
| From: "David S. Miller" <davem@davemloft.net> |
| |
| [ Upstream commit 21af8107f27878813d0364733c0b08813c2c192a ] |
| |
| Meelis Roos reports a crash in esp_free_lun_tag() in the presense |
| of a disk which has died. |
| |
| The issue is that when we issue an autosense command, we do so by |
| hijacking the original command that caused the check-condition. |
| |
| When we do so we clear out the ent->tag[] array when we issue it via |
| find_and_prep_issuable_command(). This is so that the autosense |
| command is forced to be issued non-tagged. |
| |
| That is problematic, because it is the value of ent->tag[] which |
| determines whether we issued the original scsi command as tagged |
| vs. non-tagged (see esp_alloc_lun_tag()). |
| |
| And that, in turn, is what trips up the sanity checks in |
| esp_free_lun_tag(). That function needs the original ->tag[] values |
| in order to free up the tag slot properly. |
| |
| Fix this by remembering the original command's tag values, and |
| having esp_alloc_lun_tag() and esp_free_lun_tag() use them. |
| |
| Reported-by: Meelis Roos <mroos@linux.ee> |
| Tested-by: Meelis Roos <mroos@linux.ee> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/scsi/esp_scsi.c | 14 ++++++++------ |
| drivers/scsi/esp_scsi.h | 1 + |
| 2 files changed, 9 insertions(+), 6 deletions(-) |
| |
| --- a/drivers/scsi/esp_scsi.c |
| +++ b/drivers/scsi/esp_scsi.c |
| @@ -530,7 +530,7 @@ static int esp_need_to_nego_sync(struct |
| static int esp_alloc_lun_tag(struct esp_cmd_entry *ent, |
| struct esp_lun_data *lp) |
| { |
| - if (!ent->tag[0]) { |
| + if (!ent->orig_tag[0]) { |
| /* Non-tagged, slot already taken? */ |
| if (lp->non_tagged_cmd) |
| return -EBUSY; |
| @@ -564,9 +564,9 @@ static int esp_alloc_lun_tag(struct esp_ |
| return -EBUSY; |
| } |
| |
| - BUG_ON(lp->tagged_cmds[ent->tag[1]]); |
| + BUG_ON(lp->tagged_cmds[ent->orig_tag[1]]); |
| |
| - lp->tagged_cmds[ent->tag[1]] = ent; |
| + lp->tagged_cmds[ent->orig_tag[1]] = ent; |
| lp->num_tagged++; |
| |
| return 0; |
| @@ -575,9 +575,9 @@ static int esp_alloc_lun_tag(struct esp_ |
| static void esp_free_lun_tag(struct esp_cmd_entry *ent, |
| struct esp_lun_data *lp) |
| { |
| - if (ent->tag[0]) { |
| - BUG_ON(lp->tagged_cmds[ent->tag[1]] != ent); |
| - lp->tagged_cmds[ent->tag[1]] = NULL; |
| + if (ent->orig_tag[0]) { |
| + BUG_ON(lp->tagged_cmds[ent->orig_tag[1]] != ent); |
| + lp->tagged_cmds[ent->orig_tag[1]] = NULL; |
| lp->num_tagged--; |
| } else { |
| BUG_ON(lp->non_tagged_cmd != ent); |
| @@ -667,6 +667,8 @@ static struct esp_cmd_entry *find_and_pr |
| ent->tag[0] = 0; |
| ent->tag[1] = 0; |
| } |
| + ent->orig_tag[0] = ent->tag[0]; |
| + ent->orig_tag[1] = ent->tag[1]; |
| |
| if (esp_alloc_lun_tag(ent, lp) < 0) |
| continue; |
| --- a/drivers/scsi/esp_scsi.h |
| +++ b/drivers/scsi/esp_scsi.h |
| @@ -271,6 +271,7 @@ struct esp_cmd_entry { |
| #define ESP_CMD_FLAG_AUTOSENSE 0x04 /* Doing automatic REQUEST_SENSE */ |
| |
| u8 tag[2]; |
| + u8 orig_tag[2]; |
| |
| u8 status; |
| u8 message; |