@@ -270,6 +270,208 @@ static void memset_test(struct kunit *test)
270
270
#undef TEST_OP
271
271
}
272
272
273
+ static u8 large_src [1024 ];
274
+ static u8 large_dst [2048 ];
275
+ static const u8 large_zero [2048 ];
276
+
277
+ static void set_random_nonzero (struct kunit * test , u8 * byte )
278
+ {
279
+ int failed_rng = 0 ;
280
+
281
+ while (* byte == 0 ) {
282
+ get_random_bytes (byte , 1 );
283
+ KUNIT_ASSERT_LT_MSG (test , failed_rng ++ , 100 ,
284
+ "Is the RNG broken?" );
285
+ }
286
+ }
287
+
288
+ static void init_large (struct kunit * test )
289
+ {
290
+
291
+ /* Get many bit patterns. */
292
+ get_random_bytes (large_src , ARRAY_SIZE (large_src ));
293
+
294
+ /* Make sure we have non-zero edges. */
295
+ set_random_nonzero (test , & large_src [0 ]);
296
+ set_random_nonzero (test , & large_src [ARRAY_SIZE (large_src ) - 1 ]);
297
+
298
+ /* Explicitly zero the entire destination. */
299
+ memset (large_dst , 0 , ARRAY_SIZE (large_dst ));
300
+ }
301
+
302
+ /*
303
+ * Instead of an indirect function call for "copy" or a giant macro,
304
+ * use a bool to pick memcpy or memmove.
305
+ */
306
+ static void copy_large_test (struct kunit * test , bool use_memmove )
307
+ {
308
+ init_large (test );
309
+
310
+ /* Copy a growing number of non-overlapping bytes ... */
311
+ for (int bytes = 1 ; bytes <= ARRAY_SIZE (large_src ); bytes ++ ) {
312
+ /* Over a shifting destination window ... */
313
+ for (int offset = 0 ; offset < ARRAY_SIZE (large_src ); offset ++ ) {
314
+ int right_zero_pos = offset + bytes ;
315
+ int right_zero_size = ARRAY_SIZE (large_dst ) - right_zero_pos ;
316
+
317
+ /* Copy! */
318
+ if (use_memmove )
319
+ memmove (large_dst + offset , large_src , bytes );
320
+ else
321
+ memcpy (large_dst + offset , large_src , bytes );
322
+
323
+ /* Did we touch anything before the copy area? */
324
+ KUNIT_ASSERT_EQ_MSG (test ,
325
+ memcmp (large_dst , large_zero , offset ), 0 ,
326
+ "with size %d at offset %d" , bytes , offset );
327
+ /* Did we touch anything after the copy area? */
328
+ KUNIT_ASSERT_EQ_MSG (test ,
329
+ memcmp (& large_dst [right_zero_pos ], large_zero , right_zero_size ), 0 ,
330
+ "with size %d at offset %d" , bytes , offset );
331
+
332
+ /* Are we byte-for-byte exact across the copy? */
333
+ KUNIT_ASSERT_EQ_MSG (test ,
334
+ memcmp (large_dst + offset , large_src , bytes ), 0 ,
335
+ "with size %d at offset %d" , bytes , offset );
336
+
337
+ /* Zero out what we copied for the next cycle. */
338
+ memset (large_dst + offset , 0 , bytes );
339
+ }
340
+ /* Avoid stall warnings if this loop gets slow. */
341
+ cond_resched ();
342
+ }
343
+ }
344
+
345
+ static void memcpy_large_test (struct kunit * test )
346
+ {
347
+ copy_large_test (test , false);
348
+ }
349
+
350
+ static void memmove_large_test (struct kunit * test )
351
+ {
352
+ copy_large_test (test , true);
353
+ }
354
+
355
+ /*
356
+ * On the assumption that boundary conditions are going to be the most
357
+ * sensitive, instead of taking a full step (inc) each iteration,
358
+ * take single index steps for at least the first "inc"-many indexes
359
+ * from the "start" and at least the last "inc"-many indexes before
360
+ * the "end". When in the middle, take full "inc"-wide steps. For
361
+ * example, calling next_step(idx, 1, 15, 3) with idx starting at 0
362
+ * would see the following pattern: 1 2 3 4 7 10 11 12 13 14 15.
363
+ */
364
+ static int next_step (int idx , int start , int end , int inc )
365
+ {
366
+ start += inc ;
367
+ end -= inc ;
368
+
369
+ if (idx < start || idx + inc > end )
370
+ inc = 1 ;
371
+ return idx + inc ;
372
+ }
373
+
374
+ static void inner_loop (struct kunit * test , int bytes , int d_off , int s_off )
375
+ {
376
+ int left_zero_pos , left_zero_size ;
377
+ int right_zero_pos , right_zero_size ;
378
+ int src_pos , src_orig_pos , src_size ;
379
+ int pos ;
380
+
381
+ /* Place the source in the destination buffer. */
382
+ memcpy (& large_dst [s_off ], large_src , bytes );
383
+
384
+ /* Copy to destination offset. */
385
+ memmove (& large_dst [d_off ], & large_dst [s_off ], bytes );
386
+
387
+ /* Make sure destination entirely matches. */
388
+ KUNIT_ASSERT_EQ_MSG (test , memcmp (& large_dst [d_off ], large_src , bytes ), 0 ,
389
+ "with size %d at src offset %d and dest offset %d" ,
390
+ bytes , s_off , d_off );
391
+
392
+ /* Calculate the expected zero spans. */
393
+ if (s_off < d_off ) {
394
+ left_zero_pos = 0 ;
395
+ left_zero_size = s_off ;
396
+
397
+ right_zero_pos = d_off + bytes ;
398
+ right_zero_size = ARRAY_SIZE (large_dst ) - right_zero_pos ;
399
+
400
+ src_pos = s_off ;
401
+ src_orig_pos = 0 ;
402
+ src_size = d_off - s_off ;
403
+ } else {
404
+ left_zero_pos = 0 ;
405
+ left_zero_size = d_off ;
406
+
407
+ right_zero_pos = s_off + bytes ;
408
+ right_zero_size = ARRAY_SIZE (large_dst ) - right_zero_pos ;
409
+
410
+ src_pos = d_off + bytes ;
411
+ src_orig_pos = src_pos - s_off ;
412
+ src_size = right_zero_pos - src_pos ;
413
+ }
414
+
415
+ /* Check non-overlapping source is unchanged.*/
416
+ KUNIT_ASSERT_EQ_MSG (test ,
417
+ memcmp (& large_dst [src_pos ], & large_src [src_orig_pos ], src_size ), 0 ,
418
+ "with size %d at src offset %d and dest offset %d" ,
419
+ bytes , s_off , d_off );
420
+
421
+ /* Check leading buffer contents are zero. */
422
+ KUNIT_ASSERT_EQ_MSG (test ,
423
+ memcmp (& large_dst [left_zero_pos ], large_zero , left_zero_size ), 0 ,
424
+ "with size %d at src offset %d and dest offset %d" ,
425
+ bytes , s_off , d_off );
426
+ /* Check trailing buffer contents are zero. */
427
+ KUNIT_ASSERT_EQ_MSG (test ,
428
+ memcmp (& large_dst [right_zero_pos ], large_zero , right_zero_size ), 0 ,
429
+ "with size %d at src offset %d and dest offset %d" ,
430
+ bytes , s_off , d_off );
431
+
432
+ /* Zero out everything not already zeroed.*/
433
+ pos = left_zero_pos + left_zero_size ;
434
+ memset (& large_dst [pos ], 0 , right_zero_pos - pos );
435
+ }
436
+
437
+ static void memmove_overlap_test (struct kunit * test )
438
+ {
439
+ /*
440
+ * Running all possible offset and overlap combinations takes a
441
+ * very long time. Instead, only check up to 128 bytes offset
442
+ * into the destination buffer (which should result in crossing
443
+ * cachelines), with a step size of 1 through 7 to try to skip some
444
+ * redundancy.
445
+ */
446
+ static const int offset_max = 128 ; /* less than ARRAY_SIZE(large_src); */
447
+ static const int bytes_step = 7 ;
448
+ static const int window_step = 7 ;
449
+
450
+ static const int bytes_start = 1 ;
451
+ static const int bytes_end = ARRAY_SIZE (large_src ) + 1 ;
452
+
453
+ init_large (test );
454
+
455
+ /* Copy a growing number of overlapping bytes ... */
456
+ for (int bytes = bytes_start ; bytes < bytes_end ;
457
+ bytes = next_step (bytes , bytes_start , bytes_end , bytes_step )) {
458
+
459
+ /* Over a shifting destination window ... */
460
+ for (int d_off = 0 ; d_off < offset_max ; d_off ++ ) {
461
+ int s_start = max (d_off - bytes , 0 );
462
+ int s_end = min_t (int , d_off + bytes , ARRAY_SIZE (large_src ));
463
+
464
+ /* Over a shifting source window ... */
465
+ for (int s_off = s_start ; s_off < s_end ;
466
+ s_off = next_step (s_off , s_start , s_end , window_step ))
467
+ inner_loop (test , bytes , d_off , s_off );
468
+
469
+ /* Avoid stall warnings. */
470
+ cond_resched ();
471
+ }
472
+ }
473
+ }
474
+
273
475
static void strtomem_test (struct kunit * test )
274
476
{
275
477
static const char input [sizeof (unsigned long )] = "hi" ;
@@ -325,7 +527,10 @@ static void strtomem_test(struct kunit *test)
325
527
static struct kunit_case memcpy_test_cases [] = {
326
528
KUNIT_CASE (memset_test ),
327
529
KUNIT_CASE (memcpy_test ),
530
+ KUNIT_CASE (memcpy_large_test ),
328
531
KUNIT_CASE (memmove_test ),
532
+ KUNIT_CASE (memmove_large_test ),
533
+ KUNIT_CASE (memmove_overlap_test ),
329
534
KUNIT_CASE (strtomem_test ),
330
535
{}
331
536
};
0 commit comments