Server-side copy with sendfile system call

Teng-Feng Yang shinrairis at gmail.com
Thu May 22 04:37:34 MDT 2014


Hi David,

I already reimplement the vfswrap_copy_chunk_send with sendfile system call.
However, I have noticed that the throughput shown in my Windows Server
2012 machine keeps bouncing up and down through the whole copy
process.
The peak performance is around 140MB/s on a single 4TB harddisk, and
then it will drop down to 0MB/s for a couple of seconds before it
rises back to 140MB/s again.
This repeat pattern results in an overall worse performance than the
origin read-then-write version of copy_chunk function.

I don't know if I do anything wrong. The following is my version of
copy_chunk_send using sendfile().
Any comment would be highly appreciated.
Thanks.

Dennis

static struct tevent_req *vfswrap_copy_chunk_send(struct
vfs_handle_struct *handle,
                          TALLOC_CTX *mem_ctx,
                          struct tevent_context *ev,
                          struct files_struct *src_fsp,
                          off_t src_off,
                          struct files_struct *dest_fsp,
                          off_t dest_off,
                          off_t num)
{
    struct tevent_req *req;
    struct vfs_cc_state *vfs_cc_state;
    off_t dcur_off;
    ssize_t ret;
    struct lock_struct src_lck, dest_lck;
    int saved_errno;
    NTSTATUS status;

    DEBUG(10, ("performing server side copy chunk of length %lu\n",
           (unsigned long)num));

    req = tevent_req_create(mem_ctx, &vfs_cc_state, struct vfs_cc_state);
    if (req == NULL) {
        return NULL;
    }

    status = vfs_stat_fsp(src_fsp);
    if (tevent_req_nterror(req, status)) {
        return tevent_req_post(req, ev);
    }

    if (src_fsp->fsp_name->st.st_ex_size < src_off + num) {
        /*
         * [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request
         *   If the SourceOffset or SourceOffset + Length extends beyond
         *   the end of file, the server SHOULD<240> treat this as a
         *   STATUS_END_OF_FILE error.
         * ...
         *   <240> Section 3.3.5.15.6: Windows servers will return
         *   STATUS_INVALID_VIEW_SIZE instead of STATUS_END_OF_FILE.
         */
        tevent_req_nterror(req, NT_STATUS_INVALID_VIEW_SIZE);
        return tevent_req_post(req, ev);
    }

    /* could use 2.6.33+ sendfile here to do this in kernel */
    init_strict_lock_struct(src_fsp,
                src_fsp->op->global->open_persistent_id,
                src_off,
                num,
                READ_LOCK,
                &src_lck);

    init_strict_lock_struct(dest_fsp,
                dest_fsp->op->global->open_persistent_id,
                dest_off,
                num,
                WRITE_LOCK,
                &dest_lck);

    if (!SMB_VFS_STRICT_LOCK(src_fsp->conn, src_fsp, &src_lck)) {
        tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
        return tevent_req_post(req, ev);
    }

    if (!SMB_VFS_STRICT_LOCK(dest_fsp->conn, dest_fsp, &dest_lck)) {
        SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
        tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
        return tevent_req_post(req, ev);
    }

    dcur_off = SMB_VFS_LSEEK(dest_fsp, 0, SEEK_CUR);

    if (dcur_off == -1) {
        SMB_VFS_STRICT_UNLOCK(dest_fsp->conn, dest_fsp, &dest_lck);
        SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
        tevent_req_nterror(req, map_nt_error_from_unix(errno));
        return tevent_req_post(req, ev);
    }

    if (SMB_VFS_LSEEK(dest_fsp, dest_off, SEEK_SET) == -1) {
        SMB_VFS_STRICT_UNLOCK(dest_fsp->conn, dest_fsp, &dest_lck);
        SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
        tevent_req_nterror(req, map_nt_error_from_unix(errno));
        return tevent_req_post(req, ev);
    }

    ret = SMB_VFS_SENDFILE(dest_fsp->fh->fd, src_fsp, NULL, src_off, num);
    if (ret == -1) {
        saved_errno = errno;
    }

    DEBUG(10, ("sendfile return %lu\n",
           (unsigned long)ret));
    SMB_VFS_LSEEK(dest_fsp, dcur_off, SEEK_SET);

    SMB_VFS_STRICT_UNLOCK(dest_fsp->conn, dest_fsp, &dest_lck);
    SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);

    if (ret == -1) {
        errno = saved_errno;
        tevent_req_nterror(req, map_nt_error_from_unix(errno));
        return tevent_req_post(req, ev);
    }

    if (ret != num) {
        /* zero tolerance for short reads */
        tevent_req_nterror(req, NT_STATUS_IO_DEVICE_ERROR);
        return tevent_req_post(req, ev);
    }

    vfs_cc_state->copied = num;

    tevent_req_done(req);
    return tevent_req_post(req, ev);
}


2014-05-15 15:37 GMT+08:00 David Disseldorp <ddiss at suse.de>:
> Hi Dennis,
>
> On Thu, 15 May 2014 11:39:26 +0800, Teng-Feng Yang wrote:
>
> ...
>> Thanks for the tips.
>> I write a small program to test sendfile, and it turns out that
>> sendfile will do nothing when out_fd == in_fd.
>> Does this mean that we should fallback to read-then-write procedure to
>> deal with this condition?
>
> Yes, a fall-back would be needed for this case, as Windows handles such
> requests successfully. The test_ioctl_copy_chunk_src_is_dest smbtorture
> test covers this case.
>
> Cheers, David
>


More information about the samba-technical mailing list