@@ -103,19 +103,38 @@ void vduse_domain_clear_map(struct vduse_iova_domain *domain,
103103static int vduse_domain_map_bounce_page (struct vduse_iova_domain * domain ,
104104 u64 iova , u64 size , u64 paddr )
105105{
106- struct vduse_bounce_map * map ;
106+ struct vduse_bounce_map * map , * head_map ;
107+ struct page * tmp_page ;
107108 u64 last = iova + size - 1 ;
108109
109110 while (iova <= last ) {
110- map = & domain -> bounce_maps [iova >> PAGE_SHIFT ];
111+ /*
112+ * When PAGE_SIZE is larger than 4KB, multiple adjacent bounce_maps will
113+ * point to the same memory page of PAGE_SIZE. Since bounce_maps originate
114+ * from IO requests, we may not be able to guarantee that the orig_phys
115+ * values of all IO requests within the same 64KB memory page are contiguous.
116+ * Therefore, we need to store them separately.
117+ *
118+ * Bounce pages are allocated on demand. As a result, it may occur that
119+ * multiple bounce pages corresponding to the same 64KB memory page attempt
120+ * to allocate memory simultaneously, so we use cmpxchg to handle this
121+ * concurrency.
122+ */
123+ map = & domain -> bounce_maps [iova >> BOUNCE_MAP_SHIFT ];
111124 if (!map -> bounce_page ) {
112- map -> bounce_page = alloc_page (GFP_ATOMIC );
113- if (!map -> bounce_page )
114- return - ENOMEM ;
125+ head_map = & domain -> bounce_maps [(iova & PAGE_MASK ) >> BOUNCE_MAP_SHIFT ];
126+ if (!head_map -> bounce_page ) {
127+ tmp_page = alloc_page (GFP_ATOMIC );
128+ if (!tmp_page )
129+ return - ENOMEM ;
130+ if (cmpxchg (& head_map -> bounce_page , NULL , tmp_page ))
131+ __free_page (tmp_page );
132+ }
133+ map -> bounce_page = head_map -> bounce_page ;
115134 }
116135 map -> orig_phys = paddr ;
117- paddr += PAGE_SIZE ;
118- iova += PAGE_SIZE ;
136+ paddr += BOUNCE_MAP_SIZE ;
137+ iova += BOUNCE_MAP_SIZE ;
119138 }
120139 return 0 ;
121140}
@@ -127,12 +146,17 @@ static void vduse_domain_unmap_bounce_page(struct vduse_iova_domain *domain,
127146 u64 last = iova + size - 1 ;
128147
129148 while (iova <= last ) {
130- map = & domain -> bounce_maps [iova >> PAGE_SHIFT ];
149+ map = & domain -> bounce_maps [iova >> BOUNCE_MAP_SHIFT ];
131150 map -> orig_phys = INVALID_PHYS_ADDR ;
132- iova += PAGE_SIZE ;
151+ iova += BOUNCE_MAP_SIZE ;
133152 }
134153}
135154
155+ static unsigned int offset_in_bounce_page (dma_addr_t addr )
156+ {
157+ return (addr & ~BOUNCE_MAP_MASK );
158+ }
159+
136160static void do_bounce (phys_addr_t orig , void * addr , size_t size ,
137161 enum dma_data_direction dir )
138162{
@@ -163,17 +187,18 @@ static void vduse_domain_bounce(struct vduse_iova_domain *domain,
163187{
164188 struct vduse_bounce_map * map ;
165189 struct page * page ;
166- unsigned int offset ;
190+ unsigned int offset , head_offset ;
167191 void * addr ;
168192 size_t sz ;
169193
170194 if (iova >= domain -> bounce_size )
171195 return ;
172196
173197 while (size ) {
174- map = & domain -> bounce_maps [iova >> PAGE_SHIFT ];
175- offset = offset_in_page (iova );
176- sz = min_t (size_t , PAGE_SIZE - offset , size );
198+ map = & domain -> bounce_maps [iova >> BOUNCE_MAP_SHIFT ];
199+ head_offset = offset_in_page (iova );
200+ offset = offset_in_bounce_page (iova );
201+ sz = min_t (size_t , BOUNCE_MAP_SIZE - offset , size );
177202
178203 if (WARN_ON (!map -> bounce_page ||
179204 map -> orig_phys == INVALID_PHYS_ADDR ))
@@ -183,7 +208,7 @@ static void vduse_domain_bounce(struct vduse_iova_domain *domain,
183208 map -> user_bounce_page : map -> bounce_page ;
184209
185210 addr = kmap_local_page (page );
186- do_bounce (map -> orig_phys + offset , addr + offset , sz , dir );
211+ do_bounce (map -> orig_phys + offset , addr + head_offset , sz , dir );
187212 kunmap_local (addr );
188213 size -= sz ;
189214 iova += sz ;
@@ -218,7 +243,7 @@ vduse_domain_get_bounce_page(struct vduse_iova_domain *domain, u64 iova)
218243 struct page * page = NULL ;
219244
220245 read_lock (& domain -> bounce_lock );
221- map = & domain -> bounce_maps [iova >> PAGE_SHIFT ];
246+ map = & domain -> bounce_maps [iova >> BOUNCE_MAP_SHIFT ];
222247 if (domain -> user_bounce_pages || !map -> bounce_page )
223248 goto out ;
224249
@@ -236,7 +261,7 @@ vduse_domain_free_kernel_bounce_pages(struct vduse_iova_domain *domain)
236261 struct vduse_bounce_map * map ;
237262 unsigned long pfn , bounce_pfns ;
238263
239- bounce_pfns = domain -> bounce_size >> PAGE_SHIFT ;
264+ bounce_pfns = domain -> bounce_size >> BOUNCE_MAP_SHIFT ;
240265
241266 for (pfn = 0 ; pfn < bounce_pfns ; pfn ++ ) {
242267 map = & domain -> bounce_maps [pfn ];
@@ -246,16 +271,21 @@ vduse_domain_free_kernel_bounce_pages(struct vduse_iova_domain *domain)
246271 if (!map -> bounce_page )
247272 continue ;
248273
249- __free_page (map -> bounce_page );
274+ if (!((pfn << BOUNCE_MAP_SHIFT ) & ~PAGE_MASK ))
275+ __free_page (map -> bounce_page );
250276 map -> bounce_page = NULL ;
251277 }
252278}
253279
254280int vduse_domain_add_user_bounce_pages (struct vduse_iova_domain * domain ,
255281 struct page * * pages , int count )
256282{
257- struct vduse_bounce_map * map ;
258- int i , ret ;
283+ struct vduse_bounce_map * map , * head_map ;
284+ int i , j , ret ;
285+ int inner_pages = PAGE_SIZE / BOUNCE_MAP_SIZE ;
286+ int bounce_pfns = domain -> bounce_size >> BOUNCE_MAP_SHIFT ;
287+ struct page * head_page = NULL ;
288+ bool need_copy ;
259289
260290 /* Now we don't support partial mapping */
261291 if (count != (domain -> bounce_size >> PAGE_SHIFT ))
@@ -267,16 +297,23 @@ int vduse_domain_add_user_bounce_pages(struct vduse_iova_domain *domain,
267297 goto out ;
268298
269299 for (i = 0 ; i < count ; i ++ ) {
270- map = & domain -> bounce_maps [i ];
271- if (map -> bounce_page ) {
300+ need_copy = false;
301+ head_map = & domain -> bounce_maps [(i * inner_pages )];
302+ head_page = head_map -> bounce_page ;
303+ for (j = 0 ; j < inner_pages ; j ++ ) {
304+ if ((i * inner_pages + j ) >= bounce_pfns )
305+ break ;
306+ map = & domain -> bounce_maps [(i * inner_pages + j )];
272307 /* Copy kernel page to user page if it's in use */
273- if (map -> orig_phys != INVALID_PHYS_ADDR )
274- memcpy_to_page (pages [i ], 0 ,
275- page_address (map -> bounce_page ),
276- PAGE_SIZE );
308+ if ((head_page ) && (map -> orig_phys != INVALID_PHYS_ADDR ))
309+ need_copy = true;
310+ map -> user_bounce_page = pages [i ];
277311 }
278- map -> user_bounce_page = pages [i ];
279312 get_page (pages [i ]);
313+ if ((head_page ) && (need_copy ))
314+ memcpy_to_page (pages [i ], 0 ,
315+ page_address (head_page ),
316+ PAGE_SIZE );
280317 }
281318 domain -> user_bounce_pages = true;
282319 ret = 0 ;
@@ -288,29 +325,40 @@ int vduse_domain_add_user_bounce_pages(struct vduse_iova_domain *domain,
288325
289326void vduse_domain_remove_user_bounce_pages (struct vduse_iova_domain * domain )
290327{
291- struct vduse_bounce_map * map ;
292- unsigned long i , count ;
328+ struct vduse_bounce_map * map , * head_map ;
329+ unsigned long i , j , count ;
330+ int inner_pages = PAGE_SIZE / BOUNCE_MAP_SIZE ;
331+ int bounce_pfns = domain -> bounce_size >> BOUNCE_MAP_SHIFT ;
332+ struct page * head_page = NULL ;
333+ bool need_copy ;
293334
294335 write_lock (& domain -> bounce_lock );
295336 if (!domain -> user_bounce_pages )
296337 goto out ;
297338
298339 count = domain -> bounce_size >> PAGE_SHIFT ;
299340 for (i = 0 ; i < count ; i ++ ) {
300- struct page * page = NULL ;
301-
302- map = & domain -> bounce_maps [i ];
303- if (WARN_ON (!map -> user_bounce_page ))
341+ need_copy = false;
342+ head_map = & domain -> bounce_maps [(i * inner_pages )];
343+ if (WARN_ON (!head_map -> user_bounce_page ))
304344 continue ;
305-
306- /* Copy user page to kernel page if it's in use */
307- if (map -> orig_phys != INVALID_PHYS_ADDR ) {
308- page = map -> bounce_page ;
309- memcpy_from_page (page_address (page ),
310- map -> user_bounce_page , 0 , PAGE_SIZE );
345+ head_page = head_map -> user_bounce_page ;
346+
347+ for (j = 0 ; j < inner_pages ; j ++ ) {
348+ if ((i * inner_pages + j ) >= bounce_pfns )
349+ break ;
350+ map = & domain -> bounce_maps [(i * inner_pages + j )];
351+ if (WARN_ON (!map -> user_bounce_page ))
352+ continue ;
353+ /* Copy user page to kernel page if it's in use */
354+ if ((map -> orig_phys != INVALID_PHYS_ADDR ) && (head_map -> bounce_page ))
355+ need_copy = true;
356+ map -> user_bounce_page = NULL ;
311357 }
312- put_page (map -> user_bounce_page );
313- map -> user_bounce_page = NULL ;
358+ if (need_copy )
359+ memcpy_from_page (page_address (head_map -> bounce_page ),
360+ head_page , 0 , PAGE_SIZE );
361+ put_page (head_page );
314362 }
315363 domain -> user_bounce_pages = false;
316364out :
@@ -581,7 +629,7 @@ vduse_domain_create(unsigned long iova_limit, size_t bounce_size)
581629 unsigned long pfn , bounce_pfns ;
582630 int ret ;
583631
584- bounce_pfns = PAGE_ALIGN (bounce_size ) >> PAGE_SHIFT ;
632+ bounce_pfns = PAGE_ALIGN (bounce_size ) >> BOUNCE_MAP_SHIFT ;
585633 if (iova_limit <= bounce_size )
586634 return NULL ;
587635
@@ -613,7 +661,7 @@ vduse_domain_create(unsigned long iova_limit, size_t bounce_size)
613661 rwlock_init (& domain -> bounce_lock );
614662 spin_lock_init (& domain -> iotlb_lock );
615663 init_iova_domain (& domain -> stream_iovad ,
616- PAGE_SIZE , IOVA_START_PFN );
664+ BOUNCE_MAP_SIZE , IOVA_START_PFN );
617665 ret = iova_domain_init_rcaches (& domain -> stream_iovad );
618666 if (ret )
619667 goto err_iovad_stream ;
0 commit comments