diff options
| author | Maciej Sosnowski <maciej.sosnowski@intel.com> | 2008-07-22 13:07:33 -0400 |
|---|---|---|
| committer | Dan Williams <dan.j.williams@intel.com> | 2008-07-22 13:07:33 -0400 |
| commit | 09177e85d6a0bffac8b55afd28ed8b82bd873f0b (patch) | |
| tree | ce3cb72509cc6c03bfc5d70a3ca0901a44b87fc0 | |
| parent | 2a46fa13d788364c093c4296fe01cae837aa8919 (diff) | |
I/OAT: Add watchdog/reset functionality to ioatdma
Due to occasional DMA channel hangs observed for I/OAT versions 1.2 and 2.0
a watchdog has been introduced to check every 2 seconds
if all channels progress normally.
If stuck channel is detected, driver resets it.
The reset is done in two parts. The second part is scheduled
by the first one to reinitialize the channel after the restart.
Signed-off-by: Maciej Sosnowski <maciej.sosnowski@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
| -rw-r--r-- | drivers/dma/ioat_dma.c | 267 | ||||
| -rw-r--r-- | drivers/dma/ioatdma.h | 10 |
2 files changed, 271 insertions, 6 deletions
diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index 171cad69f318..da572968a7db 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | #include <linux/dmaengine.h> | 32 | #include <linux/dmaengine.h> |
| 33 | #include <linux/delay.h> | 33 | #include <linux/delay.h> |
| 34 | #include <linux/dma-mapping.h> | 34 | #include <linux/dma-mapping.h> |
| 35 | #include <linux/workqueue.h> | ||
| 35 | #include "ioatdma.h" | 36 | #include "ioatdma.h" |
| 36 | #include "ioatdma_registers.h" | 37 | #include "ioatdma_registers.h" |
| 37 | #include "ioatdma_hw.h" | 38 | #include "ioatdma_hw.h" |
| @@ -41,11 +42,17 @@ | |||
| 41 | #define to_ioat_desc(lh) container_of(lh, struct ioat_desc_sw, node) | 42 | #define to_ioat_desc(lh) container_of(lh, struct ioat_desc_sw, node) |
| 42 | #define tx_to_ioat_desc(tx) container_of(tx, struct ioat_desc_sw, async_tx) | 43 | #define tx_to_ioat_desc(tx) container_of(tx, struct ioat_desc_sw, async_tx) |
| 43 | 44 | ||
| 45 | #define chan_num(ch) ((int)((ch)->reg_base - (ch)->device->reg_base) / 0x80) | ||
| 44 | static int ioat_pending_level = 4; | 46 | static int ioat_pending_level = 4; |
| 45 | module_param(ioat_pending_level, int, 0644); | 47 | module_param(ioat_pending_level, int, 0644); |
| 46 | MODULE_PARM_DESC(ioat_pending_level, | 48 | MODULE_PARM_DESC(ioat_pending_level, |
| 47 | "high-water mark for pushing ioat descriptors (default: 4)"); | 49 | "high-water mark for pushing ioat descriptors (default: 4)"); |
| 48 | 50 | ||
| 51 | #define RESET_DELAY msecs_to_jiffies(100) | ||
| 52 | #define WATCHDOG_DELAY round_jiffies(msecs_to_jiffies(2000)) | ||
| 53 | static void ioat_dma_chan_reset_part2(struct work_struct *work); | ||
| 54 | static void ioat_dma_chan_watchdog(struct work_struct *work); | ||
| 55 | |||
| 49 | /* internal functions */ | 56 | /* internal functions */ |
| 50 | static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan); | 57 | static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan); |
| 51 | static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan); | 58 | static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan); |
| @@ -137,6 +144,7 @@ static int ioat_dma_enumerate_channels(struct ioatdma_device *device) | |||
| 137 | ioat_chan->reg_base = device->reg_base + (0x80 * (i + 1)); | 144 | ioat_chan->reg_base = device->reg_base + (0x80 * (i + 1)); |
| 138 | ioat_chan->xfercap = xfercap; | 145 | ioat_chan->xfercap = xfercap; |
| 139 | ioat_chan->desccount = 0; | 146 | ioat_chan->desccount = 0; |
| 147 | INIT_DELAYED_WORK(&ioat_chan->work, ioat_dma_chan_reset_part2); | ||
| 140 | if (ioat_chan->device->version != IOAT_VER_1_2) { | 148 | if (ioat_chan->device->version != IOAT_VER_1_2) { |
| 141 | writel(IOAT_DCACTRL_CMPL_WRITE_ENABLE | 149 | writel(IOAT_DCACTRL_CMPL_WRITE_ENABLE |
| 142 | | IOAT_DMA_DCA_ANY_CPU, | 150 | | IOAT_DMA_DCA_ANY_CPU, |
| @@ -175,7 +183,7 @@ static void ioat1_dma_memcpy_issue_pending(struct dma_chan *chan) | |||
| 175 | { | 183 | { |
| 176 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | 184 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); |
| 177 | 185 | ||
| 178 | if (ioat_chan->pending != 0) { | 186 | if (ioat_chan->pending > 0) { |
| 179 | spin_lock_bh(&ioat_chan->desc_lock); | 187 | spin_lock_bh(&ioat_chan->desc_lock); |
| 180 | __ioat1_dma_memcpy_issue_pending(ioat_chan); | 188 | __ioat1_dma_memcpy_issue_pending(ioat_chan); |
| 181 | spin_unlock_bh(&ioat_chan->desc_lock); | 189 | spin_unlock_bh(&ioat_chan->desc_lock); |
| @@ -194,13 +202,228 @@ static void ioat2_dma_memcpy_issue_pending(struct dma_chan *chan) | |||
| 194 | { | 202 | { |
| 195 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | 203 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); |
| 196 | 204 | ||
| 197 | if (ioat_chan->pending != 0) { | 205 | if (ioat_chan->pending > 0) { |
| 198 | spin_lock_bh(&ioat_chan->desc_lock); | 206 | spin_lock_bh(&ioat_chan->desc_lock); |
| 199 | __ioat2_dma_memcpy_issue_pending(ioat_chan); | 207 | __ioat2_dma_memcpy_issue_pending(ioat_chan); |
| 200 | spin_unlock_bh(&ioat_chan->desc_lock); | 208 | spin_unlock_bh(&ioat_chan->desc_lock); |
| 201 | } | 209 | } |
| 202 | } | 210 | } |
| 203 | 211 | ||
| 212 | |||
| 213 | /** | ||
| 214 | * ioat_dma_chan_reset_part2 - reinit the channel after a reset | ||
| 215 | */ | ||
| 216 | static void ioat_dma_chan_reset_part2(struct work_struct *work) | ||
| 217 | { | ||
| 218 | struct ioat_dma_chan *ioat_chan = | ||
| 219 | container_of(work, struct ioat_dma_chan, work.work); | ||
| 220 | struct ioat_desc_sw *desc; | ||
| 221 | |||
| 222 | spin_lock_bh(&ioat_chan->cleanup_lock); | ||
| 223 | spin_lock_bh(&ioat_chan->desc_lock); | ||
| 224 | |||
| 225 | ioat_chan->completion_virt->low = 0; | ||
| 226 | ioat_chan->completion_virt->high = 0; | ||
| 227 | ioat_chan->pending = 0; | ||
| 228 | |||
| 229 | /* | ||
| 230 | * count the descriptors waiting, and be sure to do it | ||
| 231 | * right for both the CB1 line and the CB2 ring | ||
| 232 | */ | ||
| 233 | ioat_chan->dmacount = 0; | ||
| 234 | if (ioat_chan->used_desc.prev) { | ||
| 235 | desc = to_ioat_desc(ioat_chan->used_desc.prev); | ||
| 236 | do { | ||
| 237 | ioat_chan->dmacount++; | ||
| 238 | desc = to_ioat_desc(desc->node.next); | ||
| 239 | } while (&desc->node != ioat_chan->used_desc.next); | ||
| 240 | } | ||
| 241 | |||
| 242 | /* | ||
| 243 | * write the new starting descriptor address | ||
| 244 | * this puts channel engine into ARMED state | ||
| 245 | */ | ||
| 246 | desc = to_ioat_desc(ioat_chan->used_desc.prev); | ||
| 247 | switch (ioat_chan->device->version) { | ||
| 248 | case IOAT_VER_1_2: | ||
| 249 | writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, | ||
| 250 | ioat_chan->reg_base + IOAT1_CHAINADDR_OFFSET_LOW); | ||
| 251 | writel(((u64) desc->async_tx.phys) >> 32, | ||
| 252 | ioat_chan->reg_base + IOAT1_CHAINADDR_OFFSET_HIGH); | ||
| 253 | |||
| 254 | writeb(IOAT_CHANCMD_START, ioat_chan->reg_base | ||
| 255 | + IOAT_CHANCMD_OFFSET(ioat_chan->device->version)); | ||
| 256 | break; | ||
| 257 | case IOAT_VER_2_0: | ||
| 258 | writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, | ||
| 259 | ioat_chan->reg_base + IOAT2_CHAINADDR_OFFSET_LOW); | ||
| 260 | writel(((u64) desc->async_tx.phys) >> 32, | ||
| 261 | ioat_chan->reg_base + IOAT2_CHAINADDR_OFFSET_HIGH); | ||
| 262 | |||
| 263 | /* tell the engine to go with what's left to be done */ | ||
| 264 | writew(ioat_chan->dmacount, | ||
| 265 | ioat_chan->reg_base + IOAT_CHAN_DMACOUNT_OFFSET); | ||
| 266 | |||
| 267 | break; | ||
| 268 | } | ||
| 269 | dev_err(&ioat_chan->device->pdev->dev, | ||
| 270 | "chan%d reset - %d descs waiting, %d total desc\n", | ||
| 271 | chan_num(ioat_chan), ioat_chan->dmacount, ioat_chan->desccount); | ||
| 272 | |||
| 273 | spin_unlock_bh(&ioat_chan->desc_lock); | ||
| 274 | spin_unlock_bh(&ioat_chan->cleanup_lock); | ||
| 275 | } | ||
| 276 | |||
| 277 | /** | ||
| 278 | * ioat_dma_reset_channel - restart a channel | ||
| 279 | * @ioat_chan: IOAT DMA channel handle | ||
| 280 | */ | ||
| 281 | static void ioat_dma_reset_channel(struct ioat_dma_chan *ioat_chan) | ||
| 282 | { | ||
| 283 | u32 chansts, chanerr; | ||
| 284 | |||
| 285 | if (!ioat_chan->used_desc.prev) | ||
| 286 | return; | ||
| 287 | |||
| 288 | chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); | ||
| 289 | chansts = (ioat_chan->completion_virt->low | ||
| 290 | & IOAT_CHANSTS_DMA_TRANSFER_STATUS); | ||
| 291 | if (chanerr) { | ||
| 292 | dev_err(&ioat_chan->device->pdev->dev, | ||
| 293 | "chan%d, CHANSTS = 0x%08x CHANERR = 0x%04x, clearing\n", | ||
| 294 | chan_num(ioat_chan), chansts, chanerr); | ||
| 295 | writel(chanerr, ioat_chan->reg_base + IOAT_CHANERR_OFFSET); | ||
| 296 | } | ||
| 297 | |||
| 298 | /* | ||
| 299 | * whack it upside the head with a reset | ||
| 300 | * and wait for things to settle out. | ||
| 301 | * force the pending count to a really big negative | ||
| 302 | * to make sure no one forces an issue_pending | ||
| 303 | * while we're waiting. | ||
| 304 | */ | ||
| 305 | |||
| 306 | spin_lock_bh(&ioat_chan->desc_lock); | ||
| 307 | ioat_chan->pending = INT_MIN; | ||
| 308 | writeb(IOAT_CHANCMD_RESET, | ||
| 309 | ioat_chan->reg_base | ||
| 310 | + IOAT_CHANCMD_OFFSET(ioat_chan->device->version)); | ||
| 311 | spin_unlock_bh(&ioat_chan->desc_lock); | ||
| 312 | |||
| 313 | /* schedule the 2nd half instead of sleeping a long time */ | ||
| 314 | schedule_delayed_work(&ioat_chan->work, RESET_DELAY); | ||
| 315 | } | ||
| 316 | |||
| 317 | /** | ||
| 318 | * ioat_dma_chan_watchdog - watch for stuck channels | ||
| 319 | */ | ||
| 320 | static void ioat_dma_chan_watchdog(struct work_struct *work) | ||
| 321 | { | ||
| 322 | struct ioatdma_device *device = | ||
| 323 | container_of(work, struct ioatdma_device, work.work); | ||
| 324 | struct ioat_dma_chan *ioat_chan; | ||
| 325 | int i; | ||
| 326 | |||
| 327 | union { | ||
| 328 | u64 full; | ||
| 329 | struct { | ||
| 330 | u32 low; | ||
| 331 | u32 high; | ||
