-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhpsa-fix-race-between-abort-handler-and-io-path
179 lines (167 loc) · 5.86 KB
/
hpsa-fix-race-between-abort-handler-and-io-path
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
hpsa: fix race between abort handler and main i/o path
From: Stephen M. Cameron <[email protected]>
This means changing the allocator to reference count commands.
The reference count is now the authoritative indicator of whether a
command is allocated or not. The h->cmd_pool_bits bitmap is now
only a heuristic hint to speed up the allocation process, it is no
longer the authoritative record of allocated commands.
Signed-off-by: Stephen M. Cameron <[email protected]>
---
drivers/scsi/hpsa.c | 76 ++++++++++++++++++++++++++++-------------------
drivers/scsi/hpsa.h | 2 +
drivers/scsi/hpsa_cmd.h | 1 +
3 files changed, 48 insertions(+), 31 deletions(-)
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 448f1a7..81c19ac 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -4398,6 +4398,7 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
char msg[256]; /* For debug messaging. */
int ml = 0;
u32 tagupper, taglower;
+ int refcount;
/* Find the controller of the command to be aborted */
h = sdev_to_hba(sc->device);
@@ -4426,9 +4427,13 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
/* Get SCSI command to be aborted */
abort = (struct CommandList *) sc->host_scribble;
if (abort == NULL) {
- dev_err(&h->pdev->dev, "%s FAILED, Command to abort is NULL.\n",
- msg);
- return FAILED;
+ /* This can happen if the command already completed. */
+ return SUCCESS;
+ }
+ refcount = atomic_inc_return(&abort->refcount);
+ if (refcount == 1) { /* Command is done already. */
+ cmd_free(h, abort);
+ return SUCCESS;
}
hpsa_get_tag(h, abort, &taglower, &tagupper);
ml += sprintf(msg+ml, "Tag:0x%08x:%08x ", tagupper, taglower);
@@ -4450,6 +4455,7 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
dev_warn(&h->pdev->dev, "FAILED abort on device C%d:B%d:T%d:L%d\n",
h->scsi_host->host_no,
dev->bus, dev->target, dev->lun);
+ cmd_free(h, abort);
return FAILED;
}
dev_info(&h->pdev->dev, "%s REQUEST SUCCEEDED.\n", msg);
@@ -4461,19 +4467,20 @@ static int hpsa_eh_abort_handler(struct scsi_cmnd *sc)
*/
#define ABORT_COMPLETE_WAIT_SECS 30
for (i = 0; i < ABORT_COMPLETE_WAIT_SECS * 10; i++) {
- if (test_bit(abort->cmdindex & (BITS_PER_LONG - 1),
- h->cmd_pool_bits +
- (abort->cmdindex / BITS_PER_LONG)))
- msleep(100);
- else
+ refcount = atomic_read(&abort->refcount);
+ if (refcount < 2) {
+ cmd_free(h, abort);
return SUCCESS;
+ } else {
+ msleep(100);
+ }
}
dev_warn(&h->pdev->dev, "%s FAILED. Aborted command has not completed after %d seconds.\n",
msg, ABORT_COMPLETE_WAIT_SECS);
+ cmd_free(h, abort);
return FAILED;
}
-
/*
* For operations that cannot sleep, a command block is allocated at init,
* and managed by cmd_alloc() and cmd_free() using a simple bitmap to track
@@ -4486,7 +4493,8 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h)
int i;
union u64bit temp64;
dma_addr_t cmd_dma_handle, err_dma_handle;
- int loopcount;
+ int refcount;
+ unsigned long offset = 0;
/* There is some *extremely* small but non-zero chance that that
* multiple threads could get in here, and one thread could
@@ -4499,23 +4507,27 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h)
* infrequently as to be indistinguishable from never.
*/
- loopcount = 0;
- do {
- i = find_first_zero_bit(h->cmd_pool_bits, h->nr_cmds);
- if (i == h->nr_cmds)
- i = 0;
- loopcount++;
- } while (test_and_set_bit(i & (BITS_PER_LONG - 1),
- h->cmd_pool_bits + (i / BITS_PER_LONG)) != 0 &&
- loopcount < 10);
-
- /* Thread got starved? We do not expect this to ever happen. */
- if (loopcount >= 10 && i == h->nr_cmds)
- return NULL;
-
- c = h->cmd_pool + i;
- memset(c, 0, sizeof(*c));
- c->Header.tag = cpu_to_le64((u64) i << DIRECT_LOOKUP_SHIFT);
+ for (;;) {
+ i = find_next_zero_bit(h->cmd_pool_bits, h->nr_cmds, offset);
+ if (unlikely(i == h->nr_cmds)) {
+ offset = 0;
+ continue;
+ }
+ c = h->cmd_pool + i;
+ refcount = atomic_inc_return(&c->refcount);
+ if (unlikely(refcount > 1)) {
+ cmd_free(h, c); /* already in use */
+ offset = (i + 1) % h->nr_cmds;
+ continue;
+ }
+ set_bit(i & (BITS_PER_LONG - 1),
+ h->cmd_pool_bits + (i / BITS_PER_LONG));
+ break; /* it's ours now. */
+ }
+
+ /* Zero out all of commandlist except the last field, refcount */
+ memset(c, 0, offsetof(struct CommandList, refcount));
+ c->Header.tag = cpu_to_le64((u64) (i << DIRECT_LOOKUP_SHIFT));
cmd_dma_handle = h->cmd_pool_dhandle + i * sizeof(*c);
c->err_info = h->errinfo_pool + i;
memset(c->err_info, 0, sizeof(*c->err_info));
@@ -4535,11 +4547,13 @@ static struct CommandList *cmd_alloc(struct ctlr_info *h)
static void cmd_free(struct ctlr_info *h, struct CommandList *c)
{
- int i;
+ if (atomic_dec_and_test(&c->refcount)) {
+ int i;
- i = c - h->cmd_pool;
- clear_bit(i & (BITS_PER_LONG - 1),
- h->cmd_pool_bits + (i / BITS_PER_LONG));
+ i = c - h->cmd_pool;
+ clear_bit(i & (BITS_PER_LONG - 1),
+ h->cmd_pool_bits + (i / BITS_PER_LONG));
+ }
}
#ifdef CONFIG_COMPAT
diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h
index 16e1f44..3ae8548 100644
--- a/drivers/scsi/hpsa.h
+++ b/drivers/scsi/hpsa.h
@@ -296,6 +296,8 @@ struct offline_device_entry {
*/
#define SA5_DOORBELL 0x20
#define SA5_REQUEST_PORT_OFFSET 0x40
+#define SA5_REQUEST_PORT64_LO_OFFSET 0xC0
+#define SA5_REQUEST_PORT64_HI_OFFSET 0xC4
#define SA5_REPLY_INTR_MASK_OFFSET 0x34
#define SA5_REPLY_PORT_OFFSET 0x44
#define SA5_INTR_STATUS 0x30
diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h
index 47d484a..ee3678b 100644
--- a/drivers/scsi/hpsa_cmd.h
+++ b/drivers/scsi/hpsa_cmd.h
@@ -404,6 +404,7 @@ struct CommandList {
long cmdindex;
struct completion *waiting;
void *scsi_cmd;
+ atomic_t refcount; /* Must be last to avoid memset in cmd_alloc */
} __aligned(COMMANDLIST_ALIGNMENT);
/* Max S/G elements in I/O accelerator command */