1
0
mirror of https://github.com/samba-team/samba.git synced 2025-02-28 01:58:17 +03:00

s3/smbd: make copy chunk asynchronous

Just use SMB_VFS_PREAD_SEND/RECV and SMB_VFS_PWRITE_SEND/RECV in a
sensible loop.

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>

Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Tue Mar 28 21:36:18 CEST 2017 on sn-devel-144
This commit is contained in:
Ralph Boehme 2017-03-12 18:13:48 +01:00 committed by Jeremy Allison
parent a849a12438
commit 60e45a2d25

View File

@ -1592,10 +1592,23 @@ static NTSTATUS vfswrap_fset_dos_attributes(struct vfs_handle_struct *handle,
}
struct vfs_cc_state {
off_t copied;
struct tevent_context *ev;
uint8_t *buf;
bool read_lck_locked;
struct lock_struct read_lck;
bool write_lck_locked;
struct lock_struct write_lck;
struct files_struct *src_fsp;
off_t src_off;
struct files_struct *dst_fsp;
off_t dst_off;
off_t to_copy;
off_t remaining;
size_t next_io_size;
};
static NTSTATUS copy_chunk_loop(struct tevent_req *req);
static struct tevent_req *vfswrap_copy_chunk_send(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
@ -1603,23 +1616,31 @@ static struct tevent_req *vfswrap_copy_chunk_send(struct vfs_handle_struct *hand
off_t src_off,
struct files_struct *dest_fsp,
off_t dest_off,
off_t num)
off_t to_copy)
{
struct tevent_req *req;
struct vfs_cc_state *vfs_cc_state;
struct vfs_cc_state *state = NULL;
size_t num = MIN(to_copy, COPYCHUNK_MAX_TOTAL_LEN);
NTSTATUS status;
DEBUG(10, ("performing server side copy chunk of length %lu\n",
(unsigned long)num));
DBG_DEBUG("server side copy chunk of length %" PRIu64 "\n", to_copy);
req = tevent_req_create(mem_ctx, &vfs_cc_state, struct vfs_cc_state);
req = tevent_req_create(mem_ctx, &state, struct vfs_cc_state);
if (req == NULL) {
return NULL;
}
vfs_cc_state->buf = talloc_array(vfs_cc_state, uint8_t,
MIN(num, COPYCHUNK_MAX_TOTAL_LEN));
if (tevent_req_nomem(vfs_cc_state->buf, req)) {
*state = (struct vfs_cc_state) {
.ev = ev,
.src_fsp = src_fsp,
.src_off = src_off,
.dst_fsp = dest_fsp,
.dst_off = dest_off,
.to_copy = to_copy,
.remaining = to_copy,
};
state->buf = talloc_array(state, uint8_t, num);
if (tevent_req_nomem(state->buf, req)) {
return tevent_req_post(req, ev);
}
@ -1652,106 +1673,178 @@ static struct tevent_req *vfswrap_copy_chunk_send(struct vfs_handle_struct *hand
return tevent_req_post(req, ev);
}
/* could use 2.6.33+ sendfile here to do this in kernel */
while (vfs_cc_state->copied < num) {
ssize_t ret;
struct lock_struct lck;
int saved_errno;
off_t this_num = MIN(talloc_array_length(vfs_cc_state->buf),
num - vfs_cc_state->copied);
init_strict_lock_struct(src_fsp,
src_fsp->op->global->open_persistent_id,
src_off,
this_num,
READ_LOCK,
&lck);
if (!SMB_VFS_STRICT_LOCK(src_fsp->conn, src_fsp, &lck)) {
tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
return tevent_req_post(req, ev);
}
ret = SMB_VFS_PREAD(src_fsp, vfs_cc_state->buf,
this_num, src_off);
if (ret == -1) {
saved_errno = errno;
}
SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &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 != this_num) {
/* zero tolerance for short reads */
tevent_req_nterror(req, NT_STATUS_IO_DEVICE_ERROR);
return tevent_req_post(req, ev);
}
src_off += ret;
init_strict_lock_struct(dest_fsp,
dest_fsp->op->global->open_persistent_id,
dest_off,
this_num,
WRITE_LOCK,
&lck);
if (!SMB_VFS_STRICT_LOCK(dest_fsp->conn, dest_fsp, &lck)) {
tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
return tevent_req_post(req, ev);
}
ret = SMB_VFS_PWRITE(dest_fsp, vfs_cc_state->buf,
this_num, dest_off);
if (ret == -1) {
saved_errno = errno;
}
SMB_VFS_STRICT_UNLOCK(dest_fsp->conn, dest_fsp, &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 != this_num) {
/* zero tolerance for short writes */
tevent_req_nterror(req, NT_STATUS_IO_DEVICE_ERROR);
return tevent_req_post(req, ev);
}
dest_off += ret;
vfs_cc_state->copied += this_num;
status = copy_chunk_loop(req);
if (!NT_STATUS_IS_OK(status)) {
tevent_req_nterror(req, status);
return tevent_req_post(req, ev);
}
tevent_req_done(req);
return tevent_req_post(req, ev);
return req;
}
static void vfswrap_copy_chunk_read_done(struct tevent_req *subreq);
static NTSTATUS copy_chunk_loop(struct tevent_req *req)
{
struct vfs_cc_state *state = tevent_req_data(req, struct vfs_cc_state);
struct tevent_req *subreq = NULL;
bool ok;
state->next_io_size = MIN(state->remaining, talloc_array_length(state->buf));
init_strict_lock_struct(state->src_fsp,
state->src_fsp->op->global->open_persistent_id,
state->src_off,
state->next_io_size,
READ_LOCK,
&state->read_lck);
ok = SMB_VFS_STRICT_LOCK(state->src_fsp->conn,
state->src_fsp,
&state->read_lck);
if (!ok) {
return NT_STATUS_FILE_LOCK_CONFLICT;
}
subreq = SMB_VFS_PREAD_SEND(state,
state->src_fsp->conn->sconn->ev_ctx,
state->src_fsp,
state->buf,
state->next_io_size,
state->src_off);
if (subreq == NULL) {
return NT_STATUS_NO_MEMORY;
}
tevent_req_set_callback(subreq, vfswrap_copy_chunk_read_done, req);
return NT_STATUS_OK;
}
static void vfswrap_copy_chunk_write_done(struct tevent_req *subreq);
static void vfswrap_copy_chunk_read_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct vfs_cc_state *state = tevent_req_data(req, struct vfs_cc_state);
struct vfs_aio_state aio_state;
ssize_t nread;
bool ok;
SMB_VFS_STRICT_UNLOCK(state->src_fsp->conn,
state->src_fsp,
&state->read_lck);
ZERO_STRUCT(state->read_lck);
nread = SMB_VFS_PREAD_RECV(subreq, &aio_state);
TALLOC_FREE(subreq);
if (nread == -1) {
DBG_ERR("read failed: %s\n", strerror(errno));
tevent_req_nterror(req, map_nt_error_from_unix(aio_state.error));
return;
}
if (nread != state->next_io_size) {
DBG_ERR("Short read, only %zd of %zu\n",
nread, state->next_io_size);
tevent_req_nterror(req, NT_STATUS_IO_DEVICE_ERROR);
return;
}
state->src_off += nread;
init_strict_lock_struct(state->dst_fsp,
state->dst_fsp->op->global->open_persistent_id,
state->dst_off,
state->next_io_size,
WRITE_LOCK,
&state->write_lck);
ok = SMB_VFS_STRICT_LOCK(state->dst_fsp->conn,
state->dst_fsp,
&state->write_lck);
if (!ok) {
tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
return;
}
subreq = SMB_VFS_PWRITE_SEND(state,
state->ev,
state->dst_fsp,
state->buf,
state->next_io_size,
state->dst_off);
if (subreq == NULL) {
tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
return;
}
tevent_req_set_callback(subreq, vfswrap_copy_chunk_write_done, req);
}
static void vfswrap_copy_chunk_write_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct vfs_cc_state *state = tevent_req_data(req, struct vfs_cc_state);
struct vfs_aio_state aio_state;
ssize_t nwritten;
NTSTATUS status;
SMB_VFS_STRICT_UNLOCK(state->dst_fsp->conn,
state->dst_fsp,
&state->write_lck);
ZERO_STRUCT(state->write_lck);
nwritten = SMB_VFS_PWRITE_RECV(subreq, &aio_state);
TALLOC_FREE(subreq);
if (nwritten == -1) {
DBG_ERR("write failed: %s\n", strerror(errno));
tevent_req_nterror(req, map_nt_error_from_unix(aio_state.error));
return;
}
if (nwritten != state->next_io_size) {
DBG_ERR("Short write, only %zd of %zu\n", nwritten, state->next_io_size);
tevent_req_nterror(req, NT_STATUS_IO_DEVICE_ERROR);
return;
}
state->dst_off += nwritten;
if (state->remaining < nwritten) {
/* Paranoia check */
tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
return;
}
state->remaining -= nwritten;
if (state->remaining == 0) {
tevent_req_done(req);
return;
}
status = copy_chunk_loop(req);
if (!NT_STATUS_IS_OK(status)) {
tevent_req_nterror(req, status);
return;
}
return;
}
static NTSTATUS vfswrap_copy_chunk_recv(struct vfs_handle_struct *handle,
struct tevent_req *req,
off_t *copied)
{
struct vfs_cc_state *vfs_cc_state = tevent_req_data(req,
struct vfs_cc_state);
struct vfs_cc_state *state = tevent_req_data(req, struct vfs_cc_state);
NTSTATUS status;
if (tevent_req_is_nterror(req, &status)) {
DEBUG(2, ("server side copy chunk failed: %s\n",
nt_errstr(status)));
DBG_DEBUG("copy chunk failed: %s\n", nt_errstr(status));
*copied = 0;
tevent_req_received(req);
return status;
}
*copied = vfs_cc_state->copied;
DEBUG(10, ("server side copy chunk copied %lu\n",
(unsigned long)*copied));
*copied = state->to_copy;
DBG_DEBUG("copy chunk copied %lu\n", (unsigned long)*copied);
tevent_req_received(req);
return NT_STATUS_OK;