diff options
Diffstat (limited to 'lib/swiotlb.c')
| -rw-r--r-- | lib/swiotlb.c | 41 |
1 files changed, 35 insertions, 6 deletions
diff --git a/lib/swiotlb.c b/lib/swiotlb.c index 1a8050ade861..4bb5a11e18a2 100644 --- a/lib/swiotlb.c +++ b/lib/swiotlb.c | |||
| @@ -282,6 +282,15 @@ address_needs_mapping(struct device *hwdev, dma_addr_t addr) | |||
| 282 | return (addr & ~mask) != 0; | 282 | return (addr & ~mask) != 0; |
| 283 | } | 283 | } |
| 284 | 284 | ||
| 285 | static inline unsigned int is_span_boundary(unsigned int index, | ||
| 286 | unsigned int nslots, | ||
| 287 | unsigned long offset_slots, | ||
| 288 | unsigned long max_slots) | ||
| 289 | { | ||
| 290 | unsigned long offset = (offset_slots + index) & (max_slots - 1); | ||
| 291 | return offset + nslots > max_slots; | ||
| 292 | } | ||
| 293 | |||
| 285 | /* | 294 | /* |
| 286 | * Allocates bounce buffer and returns its kernel virtual address. | 295 | * Allocates bounce buffer and returns its kernel virtual address. |
| 287 | */ | 296 | */ |
| @@ -292,6 +301,16 @@ map_single(struct device *hwdev, char *buffer, size_t size, int dir) | |||
| 292 | char *dma_addr; | 301 | char *dma_addr; |
| 293 | unsigned int nslots, stride, index, wrap; | 302 | unsigned int nslots, stride, index, wrap; |
| 294 | int i; | 303 | int i; |
| 304 | unsigned long start_dma_addr; | ||
| 305 | unsigned long mask; | ||
| 306 | unsigned long offset_slots; | ||
| 307 | unsigned long max_slots; | ||
| 308 | |||
| 309 | mask = dma_get_seg_boundary(hwdev); | ||
| 310 | start_dma_addr = virt_to_bus(io_tlb_start) & mask; | ||
| 311 | |||
| 312 | offset_slots = ALIGN(start_dma_addr, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT; | ||
| 313 | max_slots = ALIGN(mask + 1, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT; | ||
| 295 | 314 | ||
| 296 | /* | 315 | /* |
| 297 | * For mappings greater than a page, we limit the stride (and | 316 | * For mappings greater than a page, we limit the stride (and |
| @@ -311,10 +330,17 @@ map_single(struct device *hwdev, char *buffer, size_t size, int dir) | |||
| 311 | */ | 330 | */ |
| 312 | spin_lock_irqsave(&io_tlb_lock, flags); | 331 | spin_lock_irqsave(&io_tlb_lock, flags); |
| 313 | { | 332 | { |
| 314 | wrap = index = ALIGN(io_tlb_index, stride); | 333 | index = ALIGN(io_tlb_index, stride); |
| 315 | |||
| 316 | if (index >= io_tlb_nslabs) | 334 | if (index >= io_tlb_nslabs) |
| 317 | wrap = index = 0; | 335 | index = 0; |
| 336 | |||
| 337 | while (is_span_boundary(index, nslots, offset_slots, | ||
| 338 | max_slots)) { | ||
| 339 | index += stride; | ||
| 340 | if (index >= io_tlb_nslabs) | ||
| 341 | index = 0; | ||
| 342 | } | ||
| 343 | wrap = index; | ||
| 318 | 344 | ||
| 319 | do { | 345 | do { |
| 320 | /* | 346 | /* |
| @@ -341,9 +367,12 @@ map_single(struct device *hwdev, char *buffer, size_t size, int dir) | |||
| 341 | 367 | ||
| 342 | goto found; | 368 | goto found; |
| 343 | } | 369 | } |
| 344 | index += stride; | 370 | do { |
| 345 | if (index >= io_tlb_nslabs) | 371 | index += stride; |
| 346 | index = 0; | 372 | if (index >= io_tlb_nslabs) |
| 373 | index = 0; | ||
| 374 | } while (is_span_boundary(index, nslots, offset_slots, | ||
| 375 | max_slots)); | ||
| 347 | } while (index != wrap); | 376 | } while (index != wrap); |
| 348 | 377 | ||
| 349 | spin_unlock_irqrestore(&io_tlb_lock, flags); | 378 | spin_unlock_irqrestore(&io_tlb_lock, flags); |
