Skip to content

Commit e36863a

Browse files
Dezhong Diaoralfbaechle
authored andcommitted
MIPS: HIGHMEM DMA on noncoherent MIPS32 processors
[v4: Patch applies to linux-queue.git with kmap_atomic patches: https://patchwork.kernel.org/patch/189932/ https://patchwork.kernel.org/patch/194552/ https://patchwork.kernel.org/patch/189912/ ] The MIPS DMA coherency functions do not work properly (i.e. kernel oops) when HIGHMEM pages are passed in as arguments. Use kmap_atomic() to temporarily map high pages for cache maintenance operations. Tested on a 2.6.36-rc7 1GB HIGHMEM SMP no-alias system. Signed-off-by: Dezhong Diao <dediao@cisco.com> Signed-off-by: Kevin Cernekee <cernekee@gmail.com> Cc: Dezhong Diao <dediao@cisco.com> Cc: David Daney <ddaney@caviumnetworks.com> Cc: David VomLehn <dvomlehn@cisco.com> Cc: Sergei Shtylyov <sshtylyov@mvista.com> Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/1695/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
1 parent d0be89f commit e36863a

File tree

1 file changed

+68
-46
lines changed

1 file changed

+68
-46
lines changed

arch/mips/mm/dma-default.c

Lines changed: 68 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@
1515
#include <linux/scatterlist.h>
1616
#include <linux/string.h>
1717
#include <linux/gfp.h>
18+
#include <linux/highmem.h>
1819

1920
#include <asm/cache.h>
2021
#include <asm/io.h>
2122

2223
#include <dma-coherence.h>
2324

24-
static inline unsigned long dma_addr_to_virt(struct device *dev,
25+
static inline struct page *dma_addr_to_page(struct device *dev,
2526
dma_addr_t dma_addr)
2627
{
27-
unsigned long addr = plat_dma_addr_to_phys(dev, dma_addr);
28-
29-
return (unsigned long)phys_to_virt(addr);
28+
return pfn_to_page(
29+
plat_dma_addr_to_phys(dev, dma_addr) >> PAGE_SHIFT);
3030
}
3131

3232
/*
@@ -148,33 +148,70 @@ static void mips_dma_free_coherent(struct device *dev, size_t size, void *vaddr,
148148
free_pages(addr, get_order(size));
149149
}
150150

151-
static inline void __dma_sync(unsigned long addr, size_t size,
151+
static inline void __dma_sync_virtual(void *addr, size_t size,
152152
enum dma_data_direction direction)
153153
{
154154
switch (direction) {
155155
case DMA_TO_DEVICE:
156-
dma_cache_wback(addr, size);
156+
dma_cache_wback((unsigned long)addr, size);
157157
break;
158158

159159
case DMA_FROM_DEVICE:
160-
dma_cache_inv(addr, size);
160+
dma_cache_inv((unsigned long)addr, size);
161161
break;
162162

163163
case DMA_BIDIRECTIONAL:
164-
dma_cache_wback_inv(addr, size);
164+
dma_cache_wback_inv((unsigned long)addr, size);
165165
break;
166166

167167
default:
168168
BUG();
169169
}
170170
}
171171

172+
/*
173+
* A single sg entry may refer to multiple physically contiguous
174+
* pages. But we still need to process highmem pages individually.
175+
* If highmem is not configured then the bulk of this loop gets
176+
* optimized out.
177+
*/
178+
static inline void __dma_sync(struct page *page,
179+
unsigned long offset, size_t size, enum dma_data_direction direction)
180+
{
181+
size_t left = size;
182+
183+
do {
184+
size_t len = left;
185+
186+
if (PageHighMem(page)) {
187+
void *addr;
188+
189+
if (offset + len > PAGE_SIZE) {
190+
if (offset >= PAGE_SIZE) {
191+
page += offset >> PAGE_SHIFT;
192+
offset &= ~PAGE_MASK;
193+
}
194+
len = PAGE_SIZE - offset;
195+
}
196+
197+
addr = kmap_atomic(page);
198+
__dma_sync_virtual(addr + offset, len, direction);
199+
kunmap_atomic(addr);
200+
} else
201+
__dma_sync_virtual(page_address(page) + offset,
202+
size, direction);
203+
offset = 0;
204+
page++;
205+
left -= len;
206+
} while (left);
207+
}
208+
172209
static void mips_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
173210
size_t size, enum dma_data_direction direction, struct dma_attrs *attrs)
174211
{
175212
if (cpu_is_noncoherent_r10000(dev))
176-
__dma_sync(dma_addr_to_virt(dev, dma_addr), size,
177-
direction);
213+
__dma_sync(dma_addr_to_page(dev, dma_addr),
214+
dma_addr & ~PAGE_MASK, size, direction);
178215

179216
plat_unmap_dma_mem(dev, dma_addr, size, direction);
180217
}
@@ -185,13 +222,11 @@ static int mips_dma_map_sg(struct device *dev, struct scatterlist *sg,
185222
int i;
186223

187224
for (i = 0; i < nents; i++, sg++) {
188-
unsigned long addr;
189-
190-
addr = (unsigned long) sg_virt(sg);
191-
if (!plat_device_is_coherent(dev) && addr)
192-
__dma_sync(addr, sg->length, direction);
193-
sg->dma_address = plat_map_dma_mem(dev,
194-
(void *)addr, sg->length);
225+
if (!plat_device_is_coherent(dev))
226+
__dma_sync(sg_page(sg), sg->offset, sg->length,
227+
direction);
228+
sg->dma_address = plat_map_dma_mem_page(dev, sg_page(sg)) +
229+
sg->offset;
195230
}
196231

197232
return nents;
@@ -201,55 +236,42 @@ static dma_addr_t mips_dma_map_page(struct device *dev, struct page *page,
201236
unsigned long offset, size_t size, enum dma_data_direction direction,
202237
struct dma_attrs *attrs)
203238
{
204-
unsigned long addr;
205-
206-
addr = (unsigned long) page_address(page) + offset;
207-
208239
if (!plat_device_is_coherent(dev))
209-
__dma_sync(addr, size, direction);
240+
__dma_sync(page, offset, size, direction);
210241

211-
return plat_map_dma_mem(dev, (void *)addr, size);
242+
return plat_map_dma_mem_page(dev, page) + offset;
212243
}
213244

214245
static void mips_dma_unmap_sg(struct device *dev, struct scatterlist *sg,
215246
int nhwentries, enum dma_data_direction direction,
216247
struct dma_attrs *attrs)
217248
{
218-
unsigned long addr;
219249
int i;
220250

221251
for (i = 0; i < nhwentries; i++, sg++) {
222252
if (!plat_device_is_coherent(dev) &&
223-
direction != DMA_TO_DEVICE) {
224-
addr = (unsigned long) sg_virt(sg);
225-
if (addr)
226-
__dma_sync(addr, sg->length, direction);
227-
}
253+
direction != DMA_TO_DEVICE)
254+
__dma_sync(sg_page(sg), sg->offset, sg->length,
255+
direction);
228256
plat_unmap_dma_mem(dev, sg->dma_address, sg->length, direction);
229257
}
230258
}
231259

232260
static void mips_dma_sync_single_for_cpu(struct device *dev,
233261
dma_addr_t dma_handle, size_t size, enum dma_data_direction direction)
234262
{
235-
if (cpu_is_noncoherent_r10000(dev)) {
236-
unsigned long addr;
237-
238-
addr = dma_addr_to_virt(dev, dma_handle);
239-
__dma_sync(addr, size, direction);
240-
}
263+
if (cpu_is_noncoherent_r10000(dev))
264+
__dma_sync(dma_addr_to_page(dev, dma_handle),
265+
dma_handle & ~PAGE_MASK, size, direction);
241266
}
242267

243268
static void mips_dma_sync_single_for_device(struct device *dev,
244269
dma_addr_t dma_handle, size_t size, enum dma_data_direction direction)
245270
{
246271
plat_extra_sync_for_device(dev);
247-
if (!plat_device_is_coherent(dev)) {
248-
unsigned long addr;
249-
250-
addr = dma_addr_to_virt(dev, dma_handle);
251-
__dma_sync(addr, size, direction);
252-
}
272+
if (!plat_device_is_coherent(dev))
273+
__dma_sync(dma_addr_to_page(dev, dma_handle),
274+
dma_handle & ~PAGE_MASK, size, direction);
253275
}
254276

255277
static void mips_dma_sync_sg_for_cpu(struct device *dev,
@@ -260,8 +282,8 @@ static void mips_dma_sync_sg_for_cpu(struct device *dev,
260282
/* Make sure that gcc doesn't leave the empty loop body. */
261283
for (i = 0; i < nelems; i++, sg++) {
262284
if (cpu_is_noncoherent_r10000(dev))
263-
__dma_sync((unsigned long)page_address(sg_page(sg)),
264-
sg->length, direction);
285+
__dma_sync(sg_page(sg), sg->offset, sg->length,
286+
direction);
265287
}
266288
}
267289

@@ -273,8 +295,8 @@ static void mips_dma_sync_sg_for_device(struct device *dev,
273295
/* Make sure that gcc doesn't leave the empty loop body. */
274296
for (i = 0; i < nelems; i++, sg++) {
275297
if (!plat_device_is_coherent(dev))
276-
__dma_sync((unsigned long)page_address(sg_page(sg)),
277-
sg->length, direction);
298+
__dma_sync(sg_page(sg), sg->offset, sg->length,
299+
direction);
278300
}
279301
}
280302

@@ -295,7 +317,7 @@ void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
295317

296318
plat_extra_sync_for_device(dev);
297319
if (!plat_device_is_coherent(dev))
298-
__dma_sync((unsigned long)vaddr, size, direction);
320+
__dma_sync_virtual(vaddr, size, direction);
299321
}
300322

301323
EXPORT_SYMBOL(dma_cache_sync);

0 commit comments

Comments
 (0)