aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Warren <swarren@nvidia.com>2019-01-03 12:23:23 -0500
committerDavid S. Miller <davem@davemloft.net>2019-01-07 08:14:17 -0500
commitf65e192af35058e5c82da9e90871b472d24912bc (patch)
treeb6caf85193c35b0a88a5a0fa8aa180d956e2eeea
parentd4a7e9bb74b5aaf07b89f6531c080b1130bdf019 (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.c92
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/icm.h22
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
68static void mlx4_free_icm_coherent(struct mlx4_dev *dev, struct mlx4_icm_chunk *chunk) 68static 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
79void mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm, int coherent) 79void 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
114static int mlx4_alloc_icm_coherent(struct device *dev, struct scatterlist *mem, 114static 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;
358out: 378out:
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
363int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, 383int 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
50struct mlx4_icm_buf {
51 void *addr;
52 size_t size;
53 dma_addr_t dma_addr;
54};
55
50struct mlx4_icm_chunk { 56struct 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
57struct mlx4_icm { 67struct mlx4_icm {
@@ -114,12 +124,18 @@ static inline void mlx4_icm_next(struct mlx4_icm_iter *iter)
114 124
115static inline dma_addr_t mlx4_icm_addr(struct mlx4_icm_iter *iter) 125static 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
120static inline unsigned long mlx4_icm_size(struct mlx4_icm_iter *iter) 133static 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
125int mlx4_MAP_ICM_AUX(struct mlx4_dev *dev, struct mlx4_icm *icm); 141int mlx4_MAP_ICM_AUX(struct mlx4_dev *dev, struct mlx4_icm *icm);