Project

General

Profile

Feature #13867 ยป patch.diff

Glass_saga (Masaki Matsushita), 09/05/2017 09:06 AM

View differences:

io.c
10355 10355
    const char *notimp;
10356 10356
    rb_fdset_t fds;
10357 10357
    VALUE th;
10358
    int offload;
10358 10359
};
10359 10360

  
10360 10361
static void *
......
10484 10485
    return 0;
10485 10486
}
10486 10487

  
10488
#if defined __linux__ && defined __NR_copy_file_range
10489
#  define USE_COPY_FILE_RANGE
10490
#endif
10491

  
10492
#ifdef USE_COPY_FILE_RANGE
10493

  
10494
static ssize_t
10495
simple_copy_file_range(int in_fd, off_t *in_offset, int out_fd, off_t *out_offset, size_t count, unsigned int flags)
10496
{
10497
    return syscall(__NR_copy_file_range, in_fd, in_offset, out_fd, out_offset, count, flags);
10498
}
10499

  
10500
static int
10501
nogvl_copy_file_range(struct copy_stream_struct *stp)
10502
{
10503
    struct stat src_stat, dst_stat;
10504
    ssize_t ss;
10505
    int ret;
10506

  
10507
    off_t copy_length, src_offset, *src_offset_ptr;
10508

  
10509
    ret = fstat(stp->src_fd, &src_stat);
10510
    if (ret == -1) {
10511
        stp->syserr = "fstat";
10512
        stp->error_no = errno;
10513
        return -1;
10514
    }
10515
    if (!S_ISREG(src_stat.st_mode))
10516
        return 0;
10517

  
10518
    ret = fstat(stp->dst_fd, &dst_stat);
10519
    if (ret == -1) {
10520
        stp->syserr = "fstat";
10521
        stp->error_no = errno;
10522
        return -1;
10523
    }
10524

  
10525
    src_offset = stp->src_offset;
10526
    if (src_offset != (off_t)-1) {
10527
	src_offset_ptr = &src_offset;
10528
    }
10529
    else {
10530
	src_offset_ptr = NULL; /* if src_offset_ptr is NULL, then bytes are read from in_fd starting from the file offset */
10531
    }
10532

  
10533
    copy_length = stp->copy_length;
10534
    if (copy_length == (off_t)-1) {
10535
	if (src_offset == (off_t)-1) {
10536
	    off_t current_offset;
10537
            errno = 0;
10538
            current_offset = lseek(stp->src_fd, 0, SEEK_CUR);
10539
            if (current_offset == (off_t)-1 && errno) {
10540
                stp->syserr = "lseek";
10541
                stp->error_no = errno;
10542
                return -1;
10543
            }
10544
	    copy_length = src_stat.st_size - current_offset;
10545
	}
10546
	else {
10547
	    copy_length = src_stat.st_size - src_offset;
10548
	}
10549
    }
10550

  
10551
  retry_copy_file_range:
10552
# if SIZEOF_OFF_T > SIZEOF_SIZE_T
10553
    /* we are limited by the 32-bit ssize_t return value on 32-bit */
10554
    ss = (copy_length > (off_t)SSIZE_MAX) ? SSIZE_MAX : (ssize_t)copy_length;
10555
# else
10556
    ss = (ssize_t)copy_length;
10557
# endif
10558
    ss = simple_copy_file_range(stp->src_fd, src_offset_ptr, stp->dst_fd, NULL, ss, 0);
10559
    if (0 < ss) {
10560
        stp->total += ss;
10561
        copy_length -= ss;
10562
        if (0 < copy_length) {
10563
            goto retry_copy_file_range;
10564
        }
10565
    }
10566
    if (ss == -1) {
10567
	if (maygvl_copy_stream_continue_p(0, stp)) {
10568
            goto retry_copy_file_range;
10569
	}
10570
        switch (errno) {
10571
	  case EINVAL:
10572
#ifdef ENOSYS
10573
	  case ENOSYS:
10574
#endif
10575
#ifdef EXDEV
10576
	  case EXDEV: /* in_fd and out_fd are not on the same filesystem */
10577
#endif
10578
            return 0;
10579
	  case EAGAIN:
10580
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
10581
	  case EWOULDBLOCK:
10582
#endif
10583
            if (nogvl_copy_stream_wait_write(stp) == -1)
10584
                return -1;
10585
            goto retry_copy_file_range;
10586
        }
10587
        stp->syserr = "copy_file_range";
10588
        stp->error_no = errno;
10589
        return -1;
10590
    }
10591
    return 1;
10592
}
10593
#endif
10594

  
10487 10595
#ifdef HAVE_SENDFILE
10488 10596

  
10489 10597
# ifdef __linux__
......
10782 10890
    int ret;
10783 10891
#endif
10784 10892

  
10893
#ifdef USE_COPY_FILE_RANGE
10894
    if (stp->offload) {
10895
	ret = nogvl_copy_file_range(stp);
10896
	if (ret != 0)
10897
	    goto finish; /* error or success */
10898
    }
10899
#endif
10900

  
10785 10901
#ifdef USE_SENDFILE
10786 10902
    ret = nogvl_copy_stream_sendfile(stp);
10787 10903
    if (ret != 0)
......
11036 11152
static VALUE
11037 11153
rb_io_s_copy_stream(int argc, VALUE *argv, VALUE io)
11038 11154
{
11039
    VALUE src, dst, length, src_offset;
11155
    VALUE src, dst, length, src_offset, opt;
11040 11156
    struct copy_stream_struct st;
11041 11157

  
11042 11158
    MEMZERO(&st, struct copy_stream_struct, 1);
11043 11159

  
11044
    rb_scan_args(argc, argv, "22", &src, &dst, &length, &src_offset);
11160
    rb_scan_args(argc, argv, "22:", &src, &dst, &length, &src_offset, &opt);
11045 11161

  
11046 11162
    st.src = src;
11047 11163
    st.dst = dst;
......
11056 11172
    else
11057 11173
        st.src_offset = NUM2OFFT(src_offset);
11058 11174

  
11175
    st.offload = 0;
11176
    if (!NIL_P(opt)) {
11177
	static ID offload_id;
11178
	VALUE offload;
11179
	CONST_ID(offload_id, "offload");
11180
	rb_get_kwargs(opt, &offload_id, 0, 1, &offload);
11181
	if (offload != Qundef && RTEST(offload)) {
11182
	    st.offload = 1;
11183
	}
11184
    }
11185

  
11059 11186
    rb_fd_init(&st.fds);
11060 11187
    rb_ensure(copy_stream_body, (VALUE)&st, copy_stream_finalize, (VALUE)&st);
11061 11188

  
test/ruby/test_io.rb
366 366
    }
367 367
  end
368 368

  
369
  def test_copy_stream_small_offload
370
    mkcdtmpdir {
371
      content = "foobar"
372
      File.write("src", content)
373
      ret = IO.copy_stream("src", "dst", offload: true)
374
      assert_equal(content.bytesize, ret)
375
      assert_equal(content, File.read("dst"))
376
    }
377
  end
378

  
369 379
  def test_copy_stream_smaller
370 380
    with_srccontent {|src, content|
371 381

  
......
415 425
    }
416 426
  end
417 427

  
428
  def test_copy_stream_pipe_offload
429
    with_srccontent {|src, content|
430
      pipe(proc do |w|
431
        # it should fallback to read/write
432
        ret = IO.copy_stream(src, w, offload: true)
433
        assert_equal(content.bytesize, ret)
434
        w.close
435
      end, proc do |r|
436
        assert_equal(content, r.read)
437
      end)
438
    }
439
  end
440

  
418 441
  def test_copy_stream_write_pipe
419 442
    with_srccontent {|src, content|
420 443
      with_pipe {|r, w|
......
424 447
    }
425 448
  end
426 449

  
450
  def test_copy_stream_write_pipe_offload
451
    with_srccontent {|src, content|
452
      with_pipe {|r, w|
453
        w.close
454
        assert_raise(IOError) { IO.copy_stream(src, w, offload: true) }
455
      }
456
    }
457
  end
458

  
427 459
  def with_pipecontent
428 460
    mkcdtmpdir {
429 461
      yield "abc"
......
635 667
    }
636 668
  end
637 669

  
670
  def test_copy_stream_bigcontent_offload
671
    with_bigsrc {|bigsrc, bigcontent|
672
      ret = IO.copy_stream(bigsrc, "bigdst", offload: true)
673
      assert_equal(bigcontent.bytesize, ret)
674
      assert_equal(bigcontent, File.read("bigdst"))
675
    }
676
  end
677

  
678
  def test_copy_stream_bigcontent_mid_offload
679
    with_bigsrc {|bigsrc, bigcontent|
680
      ret = IO.copy_stream(bigsrc, "bigdst", 30000, 100, offload: true)
681
      assert_equal(30000, ret)
682
      assert_equal(bigcontent[100, 30000], File.read("bigdst"))
683
    }
684
  end
685

  
686
  def test_copy_stream_bigcontent_fpos_offload
687
    with_bigsrc {|bigsrc, bigcontent|
688
      File.open(bigsrc) {|f|
689
        begin
690
          assert_equal(0, f.pos)
691
          ret = IO.copy_stream(f, "bigdst", nil, 10, offload: true)
692
          assert_equal(bigcontent.bytesize-10, ret)
693
          assert_equal(bigcontent[10..-1], File.read("bigdst"))
694
          assert_equal(0, f.pos)
695
          ret = IO.copy_stream(f, "bigdst", 40, 30, offload: true)
696
          assert_equal(40, ret)
697
          assert_equal(bigcontent[30, 40], File.read("bigdst"))
698
          assert_equal(0, f.pos)
699
        rescue NotImplementedError
700
          #skip "pread(2) is not implemtented."
701
        end
702
      }
703
    }
704
  end
705

  
638 706
  def with_megacontent
639 707
    yield "abc" * 1234567
640 708
  end