diff options
author | Stephen Warren <swarren@nvidia.com> | 2019-01-03 12:23:23 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-01-07 08:14:17 -0500 |
commit | f65e192af35058e5c82da9e90871b472d24912bc (patch) | |
tree | b6caf85193c35b0a88a5a0fa8aa180d956e2eeea | |
parent | d4a7e9bb74b5aaf07b89f6531c080b1130bdf019 (diff) |
net/mlx4: Get rid of page operation after dma_alloc_coherent
This patch solves a crash at the time of mlx4 driver unload or system
shutdown. The crash occurs because dma_alloc_coherent() returns one
value in mlx4_alloc_icm_coherent(), but a different value is passed to
dma_free_coherent() in mlx4_free_icm_coherent(). In turn this is because
when allocated, that pointer is passed to sg_set_buf() to record it,
then when freed it is re-calculated by calling
lowmem_page_address(sg_page()) which returns a different value. Solve
this by recording the value that dma_alloc_coherent() returns, and
passing this to dma_free_coherent().
This patch is roughly equivalent to commit 378efe798ecf ("RDMA/hns: Get
rid of page operation after dma_alloc_coherent").
Based-on-code-from: Christoph Hellwig <hch@lst.de>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Reviewed-by: Tariq Toukan <tariqt@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx4/icm.c | 92 | ||||
-rw-r--r-- | drivers/net/ethernet/mellanox/mlx4/icm.h | 22 |
2 files changed, 75 insertions, 39 deletions
diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c index 4b4351141b94..76b84d08a058 100644 --- a/drivers/net/ethernet/mellanox/mlx4/icm.c +++ b/drivers/net/ethernet/mellanox/mlx4/icm.c | |||
@@ -57,12 +57,12 @@ static void mlx4_free_icm_pages(struct mlx4_dev *dev, struct mlx4_icm_chunk *chu | |||
57 | int i; | 57 | int i; |
58 | 58 | ||
59 | if (chunk->nsg > 0) | 59 | if (chunk->nsg > 0) |
60 | pci_unmap_sg(dev->persist->pdev, chunk->mem, chunk->npages, | 60 | pci_unmap_sg(dev->persist->pdev, chunk->sg, chunk->npages, |
61 | PCI_DMA_BIDIRECTIONAL); | 61 | PCI_DMA_BIDIRECTIONAL); |
62 | 62 | ||
63 | for (i = 0; i < chunk->npages; ++i) | 63 | for (i = 0; i < chunk->npages; ++i) |
64 | __free_pages(sg_page(&chunk->mem[i]), | 64 | __free_pages(sg_page(&chunk->sg[i]), |
65 | get_order(chunk->mem[i].length)); | 65 | get_order(chunk->sg[i].length)); |
66 | } | 66 | } |
67 | 67 | ||
68 | static void mlx4_free_icm_coherent(struct mlx4_dev *dev, struct mlx4_icm_chunk *chunk) | 68 | static void mlx4_free_icm_coherent(struct mlx4_dev *dev, struct mlx4_icm_chunk *chunk) |
@@ -71,9 +71,9 @@ static void mlx4_free_icm_coherent(struct mlx4_dev *dev, struct mlx4_icm_chunk * | |||
71 | 71 | ||
72 | for (i = 0; i < chunk->npages; ++i) | 72 | for (i = 0; i < chunk->npages; ++i) |
73 | dma_free_coherent(&dev->persist->pdev->dev, | 73 | dma_free_coherent(&dev->persist->pdev->dev, |
74 | chunk->mem[i].length, | 74 | chunk->buf[i].size, |
75 | lowmem_page_address(sg_page(&chunk->mem[i])), | 75 | chunk->buf[i].addr, |
76 | sg_dma_address(&chunk->mem[i])); | 76 | chunk->buf[i].dma_addr); |
77 | } | 77 | } |
78 | 78 | ||
79 | void mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm, int coherent) | 79 | void mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm, int coherent) |
@@ -111,22 +111,21 @@ static int mlx4_alloc_icm_pages(struct scatterlist *mem, int order, | |||
111 | return 0; | 111 | return 0; |
112 | } | 112 | } |
113 | 113 | ||
114 | static int mlx4_alloc_icm_coherent(struct device *dev, struct scatterlist *mem, | 114 | static int mlx4_alloc_icm_coherent(struct device *dev, struct mlx4_icm_buf *buf, |
115 | int order, gfp_t gfp_mask) | 115 | int order, gfp_t gfp_mask) |
116 | { | 116 | { |
117 | void *buf = dma_alloc_coherent(dev, PAGE_SIZE << order, | 117 | buf->addr = dma_alloc_coherent(dev, PAGE_SIZE << order, |
118 | &sg_dma_address(mem), gfp_mask); | 118 | &buf->dma_addr, gfp_mask); |
119 | if (!buf) | 119 | if (!buf->addr) |
120 | return -ENOMEM; | 120 | return -ENOMEM; |
121 | 121 | ||
122 | if (offset_in_page(buf)) { | 122 | if (offset_in_page(buf->addr)) { |
123 | dma_free_coherent(dev, PAGE_SIZE << order, | 123 | dma_free_coherent(dev, PAGE_SIZE << order, buf->addr, |
124 | buf, sg_dma_address(mem)); | 124 | buf->dma_addr); |
125 | return -ENOMEM; | 125 | return -ENOMEM; |
126 | } | 126 | } |
127 | 127 | ||
128 | sg_set_buf(mem, buf, PAGE_SIZE << order); | 128 | buf->size = PAGE_SIZE << order; |
129 | sg_dma_len(mem) = PAGE_SIZE << order; | ||
130 | return 0; | 129 | return 0; |
131 | } | 130 | } |
132 | 131 | ||
@@ -159,21 +158,21 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, | |||
159 | 158 | ||
160 | while (npages > 0) { | 159 | while (npages > 0) { |
161 | if (!chunk) { | 160 | if (!chunk) { |
162 | chunk = kmalloc_node(sizeof(*chunk), | 161 | chunk = kzalloc_node(sizeof(*chunk), |
163 | gfp_mask & ~(__GFP_HIGHMEM | | 162 | gfp_mask & ~(__GFP_HIGHMEM | |
164 | __GFP_NOWARN), | 163 | __GFP_NOWARN), |
165 | dev->numa_node); | 164 | dev->numa_node); |
166 | if (!chunk) { | 165 | if (!chunk) { |
167 | chunk = kmalloc(sizeof(*chunk), | 166 | chunk = kzalloc(sizeof(*chunk), |
168 | gfp_mask & ~(__GFP_HIGHMEM | | 167 | gfp_mask & ~(__GFP_HIGHMEM | |
169 | __GFP_NOWARN)); | 168 | __GFP_NOWARN)); |
170 | if (!chunk) | 169 | if (!chunk) |
171 | goto fail; | 170 | goto fail; |
172 | } | 171 | } |
172 | chunk->coherent = coherent; | ||
173 | 173 | ||
174 | sg_init_table(chunk->mem, MLX4_ICM_CHUNK_LEN); | 174 | if (!coherent) |
175 | chunk->npages = 0; | 175 | sg_init_table(chunk->sg, MLX4_ICM_CHUNK_LEN); |
176 | chunk->nsg = 0; | ||
177 | list_add_tail(&chunk->list, &icm->chunk_list); | 176 | list_add_tail(&chunk->list, &icm->chunk_list); |
178 | } | 177 | } |
179 | 178 | ||
@@ -186,10 +185,10 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, | |||
186 | 185 | ||
187 | if (coherent) | 186 | if (coherent) |
188 | ret = mlx4_alloc_icm_coherent(&dev->persist->pdev->dev, | 187 | ret = mlx4_alloc_icm_coherent(&dev->persist->pdev->dev, |
189 | &chunk->mem[chunk->npages], | 188 | &chunk->buf[chunk->npages], |
190 | cur_order, mask); | 189 | cur_order, mask); |
191 | else | 190 | else |
192 | ret = mlx4_alloc_icm_pages(&chunk->mem[chunk->npages], | 191 | ret = mlx4_alloc_icm_pages(&chunk->sg[chunk->npages], |
193 | cur_order, mask, | 192 | cur_order, mask, |
194 | dev->numa_node); | 193 | dev->numa_node); |
195 | 194 | ||
@@ -205,7 +204,7 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, | |||
205 | if (coherent) | 204 | if (coherent) |
206 | ++chunk->nsg; | 205 | ++chunk->nsg; |
207 | else if (chunk->npages == MLX4_ICM_CHUNK_LEN) { | 206 | else if (chunk->npages == MLX4_ICM_CHUNK_LEN) { |
208 | chunk->nsg = pci_map_sg(dev->persist->pdev, chunk->mem, | 207 | chunk->nsg = pci_map_sg(dev->persist->pdev, chunk->sg, |
209 | chunk->npages, | 208 | chunk->npages, |
210 | PCI_DMA_BIDIRECTIONAL); | 209 | PCI_DMA_BIDIRECTIONAL); |
211 | 210 | ||
@@ -220,7 +219,7 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, | |||
220 | } | 219 | } |
221 | 220 | ||
222 | if (!coherent && chunk) { | 221 | if (!coherent && chunk) { |
223 | chunk->nsg = pci_map_sg(dev->persist->pdev, chunk->mem, | 222 | chunk->nsg = pci_map_sg(dev->persist->pdev, chunk->sg, |
224 | chunk->npages, | 223 | chunk->npages, |
225 | PCI_DMA_BIDIRECTIONAL); | 224 | PCI_DMA_BIDIRECTIONAL); |
226 | 225 | ||
@@ -320,7 +319,7 @@ void *mlx4_table_find(struct mlx4_icm_table *table, u32 obj, | |||
320 | u64 idx; | 319 | u64 idx; |
321 | struct mlx4_icm_chunk *chunk; | 320 | struct mlx4_icm_chunk *chunk; |
322 | struct mlx4_icm *icm; | 321 | struct mlx4_icm *icm; |
323 | struct page *page = NULL; | 322 | void *addr = NULL; |
324 | 323 | ||
325 | if (!table->lowmem) | 324 | if (!table->lowmem) |
326 | return NULL; | 325 | return NULL; |
@@ -336,28 +335,49 @@ void *mlx4_table_find(struct mlx4_icm_table *table, u32 obj, | |||
336 | 335 | ||
337 | list_for_each_entry(chunk, &icm->chunk_list, list) { | 336 | list_for_each_entry(chunk, &icm->chunk_list, list) { |
338 | for (i = 0; i < chunk->npages; ++i) { | 337 | for (i = 0; i < chunk->npages; ++i) { |
338 | dma_addr_t dma_addr; | ||
339 | size_t len; | ||
340 | |||
341 | if (table->coherent) { | ||
342 | len = chunk->buf[i].size; | ||
343 | dma_addr = chunk->buf[i].dma_addr; | ||
344 | addr = chunk->buf[i].addr; | ||
345 | } else { | ||
346 | struct page *page; | ||
347 | |||
348 | len = sg_dma_len(&chunk->sg[i]); | ||
349 | dma_addr = sg_dma_address(&chunk->sg[i]); | ||
350 | |||
351 | /* XXX: we should never do this for highmem | ||
352 | * allocation. This function either needs | ||
353 | * to be split, or the kernel virtual address | ||
354 | * return needs to be made optional. | ||
355 | */ | ||
356 | page = sg_page(&chunk->sg[i]); | ||
357 | addr = lowmem_page_address(page); | ||
358 | } | ||
359 | |||
339 | if (dma_handle && dma_offset >= 0) { | 360 | if (dma_handle && dma_offset >= 0) { |
340 | if (sg_dma_len(&chunk->mem[i]) > dma_offset) | 361 | if (len > dma_offset) |
341 | *dma_handle = sg_dma_address(&chunk->mem[i]) + | 362 | *dma_handle = dma_addr + dma_offset; |
342 | dma_offset; | 363 | dma_offset -= len; |
343 | dma_offset -= sg_dma_len(&chunk->mem[i]); | ||
344 | } | 364 | } |
365 | |||
345 | /* | 366 | /* |
346 | * DMA mapping can merge pages but not split them, | 367 | * DMA mapping can merge pages but not split them, |
347 | * so if we found the page, dma_handle has already | 368 | * so if we found the page, dma_handle has already |
348 | * been assigned to. | 369 | * been assigned to. |
349 | */ | 370 | */ |
350 | if (chunk->mem[i].length > offset) { | 371 | if (len > offset) |
351 | page = sg_page(&chunk->mem[i]); | ||
352 | goto out; | 372 | goto out; |
353 | } | 373 | offset -= len; |
354 | offset -= chunk->mem[i].length; | ||
355 | } | 374 | } |
356 | } | 375 | } |
357 | 376 | ||
377 | addr = NULL; | ||
358 | out: | 378 | out: |
359 | mutex_unlock(&table->mutex); | 379 | mutex_unlock(&table->mutex); |
360 | return page ? lowmem_page_address(page) + offset : NULL; | 380 | return addr ? addr + offset : NULL; |
361 | } | 381 | } |
362 | 382 | ||
363 | int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, | 383 | int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, |
diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.h b/drivers/net/ethernet/mellanox/mlx4/icm.h index c9169a490557..d199874b1c07 100644 --- a/drivers/net/ethernet/mellanox/mlx4/icm.h +++ b/drivers/net/ethernet/mellanox/mlx4/icm.h | |||
@@ -47,11 +47,21 @@ enum { | |||
47 | MLX4_ICM_PAGE_SIZE = 1 << MLX4_ICM_PAGE_SHIFT, | 47 | MLX4_ICM_PAGE_SIZE = 1 << MLX4_ICM_PAGE_SHIFT, |
48 | }; | 48 | }; |
49 | 49 | ||
50 | struct mlx4_icm_buf { | ||
51 | void *addr; | ||
52 | size_t size; | ||
53 | dma_addr_t dma_addr; | ||
54 | }; | ||
55 | |||
50 | struct mlx4_icm_chunk { | 56 | struct mlx4_icm_chunk { |
51 | struct list_head list; | 57 | struct list_head list; |
52 | int npages; | 58 | int npages; |
53 | int nsg; | 59 | int nsg; |
54 | struct scatterlist mem[MLX4_ICM_CHUNK_LEN]; | 60 | bool coherent; |
61 | union { | ||
62 | struct scatterlist sg[MLX4_ICM_CHUNK_LEN]; | ||
63 | struct mlx4_icm_buf buf[MLX4_ICM_CHUNK_LEN]; | ||
64 | }; | ||
55 | }; | 65 | }; |
56 | 66 | ||
57 | struct mlx4_icm { | 67 | struct mlx4_icm { |
@@ -114,12 +124,18 @@ static inline void mlx4_icm_next(struct mlx4_icm_iter *iter) | |||
114 | 124 | ||
115 | static inline dma_addr_t mlx4_icm_addr(struct mlx4_icm_iter *iter) | 125 | static inline dma_addr_t mlx4_icm_addr(struct mlx4_icm_iter *iter) |
116 | { | 126 | { |
117 | return sg_dma_address(&iter->chunk->mem[iter->page_idx]); | 127 | if (iter->chunk->coherent) |
128 | return iter->chunk->buf[iter->page_idx].dma_addr; | ||
129 | else | ||
130 | return sg_dma_address(&iter->chunk->sg[iter->page_idx]); | ||
118 | } | 131 | } |
119 | 132 | ||
120 | static inline unsigned long mlx4_icm_size(struct mlx4_icm_iter *iter) | 133 | static inline unsigned long mlx4_icm_size(struct mlx4_icm_iter *iter) |
121 | { | 134 | { |
122 | return sg_dma_len(&iter->chunk->mem[iter->page_idx]); | 135 | if (iter->chunk->coherent) |
136 | return iter->chunk->buf[iter->page_idx].size; | ||
137 | else | ||
138 | return sg_dma_len(&iter->chunk->sg[iter->page_idx]); | ||
123 | } | 139 | } |
124 | 140 | ||
125 | int mlx4_MAP_ICM_AUX(struct mlx4_dev *dev, struct mlx4_icm *icm); | 141 | int mlx4_MAP_ICM_AUX(struct mlx4_dev *dev, struct mlx4_icm *icm); |