X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=lib%2Flibnfs.c;h=d54a542282e2e3ad2da11743e8e8d990e72d5e2d;hb=3ca2aac9a4aceac602684847f756428439bf814d;hp=5ea3280fe6c1071cb3b0af0a6187214fa2d2f478;hpb=1f1b6cb0a78dcd89299ce22abc96171d1288e074;p=deb_libnfs.git diff --git a/lib/libnfs.c b/lib/libnfs.c index 5ea3280..d54a542 100644 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -81,10 +81,22 @@ struct nfsdir { struct nfsdirent *current; }; +struct nfs_readahead { + uint64_t fh_offset; + uint64_t last_offset; + uint64_t buf_offset; + uint64_t buf_count; + time_t buf_ts; + void *buf; + uint32_t cur_ra; +}; + struct nfsfh { struct nfs_fh3 fh; int is_sync; + int is_append; uint64_t offset; + struct nfs_readahead ra; }; struct nfs_context { @@ -133,9 +145,8 @@ struct nfs_cb_data { int cancel; int oom; int num_calls; - uint64_t start_offset, max_offset; + uint64_t offset, count, max_offset, org_offset, org_count; char *buffer; - size_t request_size; char *usrbuf; }; @@ -143,6 +154,7 @@ struct nfs_mcb_data { struct nfs_cb_data *data; uint64_t offset; uint64_t count; + int update_pos; }; static int nfs_lookup_path_async_internal(struct nfs_context *nfs, struct nfs_cb_data *data, struct nfs_fh3 *fh); @@ -179,12 +191,14 @@ char *nfs_get_error(struct nfs_context *nfs) static int nfs_set_context_args(struct nfs_context *nfs, char *arg, char *val) { - if (!strncmp(arg, "tcp-syncnt", 10)) { + if (!strcmp(arg, "tcp-syncnt")) { rpc_set_tcp_syncnt(nfs_get_rpc_context(nfs), atoi(val)); - } else if (!strncmp(arg, "uid", 3)) { + } else if (!strcmp(arg, "uid")) { rpc_set_uid(nfs_get_rpc_context(nfs), atoi(val)); - } else if (!strncmp(arg, "gid", 3)) { + } else if (!strcmp(arg, "gid")) { rpc_set_gid(nfs_get_rpc_context(nfs), atoi(val)); + } else if (!strcmp(arg, "readahaed")) { + rpc_set_readahead(nfs_get_rpc_context(nfs), atoi(val)); } return 0; } @@ -1146,8 +1160,8 @@ static void nfs_stat_1_cb(struct rpc_context *rpc, int status, void *command_dat st.st_rdev = 0; st.st_size = res->GETATTR3res_u.resok.obj_attributes.size; #ifndef WIN32 - st.st_blksize = 4096; - st.st_blocks = res->GETATTR3res_u.resok.obj_attributes.size / 4096; + st.st_blksize = NFS_BLKSIZE; + st.st_blocks = res->GETATTR3res_u.resok.obj_attributes.size / NFS_BLKSIZE; #endif//WIN32 st.st_atime = res->GETATTR3res_u.resok.obj_attributes.atime.seconds; st.st_mtime = res->GETATTR3res_u.resok.obj_attributes.mtime.seconds; @@ -1306,6 +1320,9 @@ static void nfs_open_trunc_cb(struct rpc_context *rpc, int status, void *command if (data->continue_int & O_SYNC) { nfsfh->is_sync = 1; } + if (data->continue_int & O_APPEND) { + nfsfh->is_append = 1; + } /* steal the filehandle */ nfsfh->fh = data->fh; @@ -1402,6 +1419,9 @@ static void nfs_open_cb(struct rpc_context *rpc, int status, void *command_data, if (data->continue_int & O_SYNC) { nfsfh->is_sync = 1; } + if (data->continue_int & O_APPEND) { + nfsfh->is_append = 1; + } /* steal the filehandle */ nfsfh->fh = data->fh; @@ -1515,25 +1535,30 @@ static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_dat if (res->status != NFS3_OK) { rpc_set_error(nfs->rpc, "NFS: Read failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); data->error = 1; - } else { + } else { + uint64_t count = res->READ3res_u.resok.count; + + if (mdata->update_pos) + data->nfsfh->offset += count; + /* if we have more than one call or we have received a short read we need a reassembly buffer */ - if (data->num_calls || (res->READ3res_u.resok.count < mdata->count && !res->READ3res_u.resok.eof)) { + if (data->num_calls || (count < mdata->count && !res->READ3res_u.resok.eof)) { if (data->buffer == NULL) { - data->buffer = malloc(data->request_size); + data->buffer = malloc(data->count); if (data->buffer == NULL) { - rpc_set_error(nfs->rpc, "Out-Of-Memory: Failed to allocate reassembly buffer for %d bytes", (int)data->request_size); + rpc_set_error(nfs->rpc, "Out-Of-Memory: Failed to allocate reassembly buffer for %d bytes", (int)data->count); data->oom = 1; } } } - if (res->READ3res_u.resok.count > 0) { - if (res->READ3res_u.resok.count <= mdata->count) { + if (count > 0) { + if (count <= mdata->count) { /* copy data into reassembly buffer if we have one */ if (data->buffer != NULL) { - memcpy(&data->buffer[mdata->offset - data->start_offset], res->READ3res_u.resok.data.data_val, res->READ3res_u.resok.count); + memcpy(&data->buffer[mdata->offset - data->offset], res->READ3res_u.resok.data.data_val, count); } - if (data->max_offset < mdata->offset + res->READ3res_u.resok.count) { - data->max_offset = mdata->offset + res->READ3res_u.resok.count; + if (data->max_offset < mdata->offset + count) { + data->max_offset = mdata->offset + count; } } else { rpc_set_error(nfs->rpc, "NFS: Read overflow. Server has sent more data than requested!"); @@ -1541,15 +1566,15 @@ static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_dat } } /* check if we have received a short read */ - if (res->READ3res_u.resok.count < mdata->count && !res->READ3res_u.resok.eof) { - if (res->READ3res_u.resok.count == 0) { + if (count < mdata->count && !res->READ3res_u.resok.eof) { + if (count == 0) { rpc_set_error(nfs->rpc, "NFS: Read failed. No bytes read and not at EOF!"); data->error = 1; } else { /* reissue reminder of this read request */ READ3args args; - mdata->offset += res->READ3res_u.resok.count; - mdata->count -= res->READ3res_u.resok.count; + mdata->offset += count; + mdata->count -= count; nfs_fill_READ3args(&args, data->nfsfh, mdata->offset, mdata->count); if (rpc_nfs3_read_async(nfs->rpc, nfs_pread_mcb, &args, mdata) == 0) { data->num_calls++; @@ -1585,17 +1610,37 @@ static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_dat return; } - data->nfsfh->offset = data->max_offset; if (data->buffer) { - data->cb(data->max_offset - data->start_offset, nfs, data->buffer, data->private_data); + if (data->max_offset > data->org_offset + data->org_count) { + data->max_offset = data->org_offset + data->org_count; + } + data->cb(data->max_offset - data->org_offset, nfs, data->buffer + (data->org_offset - data->offset), data->private_data); } else { data->cb(res->READ3res_u.resok.count, nfs, res->READ3res_u.resok.data.data_val, data->private_data); } + data->nfsfh->ra.fh_offset = data->max_offset; + if (data->nfsfh->ra.cur_ra) { + free(data->nfsfh->ra.buf); + data->nfsfh->ra.buf = data->buffer; + data->nfsfh->ra.buf_offset = data->offset; + data->nfsfh->ra.buf_count = data->count; + data->nfsfh->ra.buf_ts = time(NULL); + data->buffer = NULL; + } free_nfs_cb_data(data); } -int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, nfs_cb cb, void *private_data) +static void nfs_ra_invalidate(struct nfsfh *nfsfh) { + free(nfsfh->ra.buf); + nfsfh->ra.buf = NULL; + nfsfh->ra.buf_offset = 0; + nfsfh->ra.buf_count = 0; + nfsfh->ra.buf_ts = time(NULL); + nfsfh->ra.cur_ra = NFS_BLKSIZE; +} + +static int nfs_pread_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, nfs_cb cb, void *private_data, int update_pos) { struct nfs_cb_data *data; @@ -1609,18 +1654,78 @@ int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offse data->cb = cb; data->private_data = private_data; data->nfsfh = nfsfh; - data->request_size = count; - - nfsfh->offset = offset; + data->org_offset = offset; + data->org_count = count; assert(data->num_calls == 0); + if (nfs->rpc->readahead && time(NULL) - nfsfh->ra.buf_ts > NFS_RA_TIMEOUT) { + /* readahead cache timeout */ + nfs_ra_invalidate(nfsfh); + } + + if (nfs->rpc->readahead) { + if (offset >= nfsfh->ra.last_offset && + offset - NFS_BLKSIZE <= nfsfh->ra.fh_offset + nfsfh->ra.cur_ra) { + if (nfs->rpc->readahead > nfsfh->ra.cur_ra) { + nfsfh->ra.cur_ra <<= 1; + } + } else { + nfsfh->ra.cur_ra = NFS_BLKSIZE; + } + + nfsfh->ra.last_offset = offset; + + if (nfsfh->ra.buf_offset <= offset && + nfsfh->ra.buf_offset + nfsfh->ra.buf_count >= offset + count) { + /* serve request completely from cache */ + data->buffer = malloc(count); + if (data->buffer == NULL) { + free_nfs_cb_data(data); + return -ENOMEM; + } + memcpy(data->buffer, nfsfh->ra.buf + (offset - nfsfh->ra.buf_offset), count); + data->cb(count, nfs, data->buffer, data->private_data); + nfsfh->ra.fh_offset = offset + count; + free_nfs_cb_data(data); + return 0; + } + + /* align start offset to blocksize */ + count += offset & (NFS_BLKSIZE - 1); + offset &= ~(NFS_BLKSIZE - 1); + + /* align end offset to blocksize and add readahead */ + count += nfsfh->ra.cur_ra - 1; + count &= ~(NFS_BLKSIZE - 1); + + data->buffer = malloc(count); + if (data->buffer == NULL) { + free_nfs_cb_data(data); + return -ENOMEM; + } + data->offset = offset; + data->count = count; + + if (nfsfh->ra.buf_count && nfsfh->ra.buf_offset <= offset && + nfsfh->ra.buf_offset + nfsfh->ra.buf_count >= offset) { + /* serve request partially from cache */ + size_t overlap = (nfsfh->ra.buf_offset + nfsfh->ra.buf_count) - offset; + if (overlap > count) count = overlap; + memcpy(data->buffer, nfsfh->ra.buf + (offset - nfsfh->ra.buf_offset), overlap); + offset += overlap; + count -= overlap; + } + } else { + data->offset = offset; + data->count = count; + } + + data->max_offset = offset; + /* chop requests into chunks of at most READMAX bytes if necessary. * we send all reads in parallel so that performance is still good. */ - data->max_offset = offset; - data->start_offset = offset; - do { uint64_t readcount = count; struct nfs_mcb_data *mdata; @@ -1644,6 +1749,7 @@ int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offse mdata->data = data; mdata->offset = offset; mdata->count = readcount; + mdata->update_pos = update_pos; nfs_fill_READ3args(&args, nfsfh, offset, readcount); @@ -1666,12 +1772,17 @@ int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offse return 0; } +int nfs_pread_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, nfs_cb cb, void *private_data) +{ + return nfs_pread_async_internal(nfs, nfsfh, offset, count, cb, private_data, 0); +} + /* * Async read() */ int nfs_read_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, nfs_cb cb, void *private_data) { - return nfs_pread_async(nfs, nfsfh, nfsfh->offset, count, cb, private_data); + return nfs_pread_async_internal(nfs, nfsfh, nfsfh->offset, count, cb, private_data, 1); } @@ -1686,7 +1797,7 @@ static void nfs_fill_WRITE3args (WRITE3args *args, struct nfsfh *fh, uint64_t of args->file = fh->fh; args->offset = offset; args->count = count; - args->stable = fh->is_sync?FILE_SYNC:UNSTABLE; + args->stable = fh->is_sync ? FILE_SYNC : UNSTABLE; args->data.data_len = count; args->data.data_val = buf; } @@ -1717,17 +1828,23 @@ static void nfs_pwrite_mcb(struct rpc_context *rpc, int status, void *command_da rpc_set_error(nfs->rpc, "NFS: Write failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); data->error = 1; } else { - if (res->WRITE3res_u.resok.count < mdata->count) { - if (res->WRITE3res_u.resok.count == 0) { + uint64_t count = res->WRITE3res_u.resok.count; + + if (mdata->update_pos) + data->nfsfh->offset += count; + + if (count < mdata->count) { + if (count == 0) { rpc_set_error(nfs->rpc, "NFS: Write failed. No bytes written!"); data->error = 1; } else { /* reissue reminder of this write request */ WRITE3args args; - mdata->offset += res->WRITE3res_u.resok.count; - mdata->count -= res->WRITE3res_u.resok.count; + mdata->offset += count; + mdata->count -= count; + nfs_fill_WRITE3args(&args, data->nfsfh, mdata->offset, mdata->count, - &data->usrbuf[mdata->offset - data->start_offset]); + &data->usrbuf[mdata->offset - data->offset]); if (rpc_nfs3_write_async(nfs->rpc, nfs_pwrite_mcb, &args, mdata) == 0) { data->num_calls++; return; @@ -1737,9 +1854,9 @@ static void nfs_pwrite_mcb(struct rpc_context *rpc, int status, void *command_da } } } - if (res->WRITE3res_u.resok.count > 0) { - if (data->max_offset < mdata->offset + res->WRITE3res_u.resok.count) { - data->max_offset = mdata->offset + res->WRITE3res_u.resok.count; + if (count > 0) { + if (data->max_offset < mdata->offset + count) { + data->max_offset = mdata->offset + count; } } } @@ -1767,14 +1884,13 @@ static void nfs_pwrite_mcb(struct rpc_context *rpc, int status, void *command_da return; } - data->nfsfh->offset = data->max_offset; - data->cb(data->max_offset - data->start_offset, nfs, NULL, data->private_data); + data->cb(data->max_offset - data->offset, nfs, NULL, data->private_data); free_nfs_cb_data(data); } -int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, char *buf, nfs_cb cb, void *private_data) +static int nfs_pwrite_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, char *buf, nfs_cb cb, void *private_data, int update_pos) { struct nfs_cb_data *data; @@ -1790,8 +1906,6 @@ int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offs data->nfsfh = nfsfh; data->usrbuf = buf; - nfsfh->offset = offset; - /* hello, clang-analyzer */ assert(data->num_calls == 0); @@ -1799,7 +1913,7 @@ int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offs * we send all writes in parallel so that performance is still good. */ data->max_offset = offset; - data->start_offset = offset; + data->offset = offset; do { uint64_t writecount = count; @@ -1824,8 +1938,9 @@ int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offs mdata->data = data; mdata->offset = offset; mdata->count = writecount; + mdata->update_pos = update_pos; - nfs_fill_WRITE3args(&args, nfsfh, offset, writecount, &buf[offset - data->start_offset]); + nfs_fill_WRITE3args(&args, nfsfh, offset, writecount, &buf[offset - data->offset]); if (rpc_nfs3_write_async(nfs->rpc, nfs_pwrite_mcb, &args, mdata) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send WRITE call for %s", data->path); @@ -1846,12 +1961,80 @@ int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offs return 0; } +int nfs_pwrite_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t offset, uint64_t count, char *buf, nfs_cb cb, void *private_data) +{ + return nfs_pwrite_async_internal(nfs, nfsfh, offset, count, buf, cb, private_data, 0); +} + /* * Async write() */ +static void nfs_write_append_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +{ + struct nfs_cb_data *data = private_data; + struct nfs_context *nfs = data->nfs; + GETATTR3res *res; + + assert(rpc->magic == RPC_CONTEXT_MAGIC); + + if (status == RPC_STATUS_ERROR) { + data->cb(-EFAULT, nfs, command_data, data->private_data); + free_nfs_cb_data(data); + return; + } + if (status == RPC_STATUS_CANCEL) { + data->cb(-EINTR, nfs, "Command was cancelled", data->private_data); + free_nfs_cb_data(data); + return; + } + + res = command_data; + if (res->status != NFS3_OK) { + rpc_set_error(nfs->rpc, "NFS: GETATTR failed with %s(%d)", nfsstat3_to_str(res->status), nfsstat3_to_errno(res->status)); + data->cb(nfsstat3_to_errno(res->status), nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + + if (nfs_pwrite_async_internal(nfs, data->nfsfh, res->GETATTR3res_u.resok.obj_attributes.size, data->count, data->usrbuf, data->cb, data->private_data, 1) != 0) { + data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); + free_nfs_cb_data(data); + return; + } + free_nfs_cb_data(data); +} + int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, char *buf, nfs_cb cb, void *private_data) { - return nfs_pwrite_async(nfs, nfsfh, nfsfh->offset, count, buf, cb, private_data); + nfs_ra_invalidate(nfsfh); + if (nfsfh->is_append) { + struct GETATTR3args args; + struct nfs_cb_data *data; + + data = malloc(sizeof(struct nfs_cb_data)); + if (data == NULL) { + rpc_set_error(nfs->rpc, "out of memory: failed to allocate nfs_cb_data structure"); + return -1; + } + memset(data, 0, sizeof(struct nfs_cb_data)); + data->nfs = nfs; + data->cb = cb; + data->private_data = private_data; + data->nfsfh = nfsfh; + data->usrbuf = buf; + data->count = count; + + memset(&args, 0, sizeof(GETATTR3args)); + args.object = nfsfh->fh; + + if (rpc_nfs3_getattr_async(nfs->rpc, nfs_write_append_cb, &args, data) != 0) { + rpc_set_error(nfs->rpc, "out of memory: failed to send GETATTR"); + free_nfs_cb_data(data); + return -1; + } + return 0; + } + return nfs_pwrite_async_internal(nfs, nfsfh, nfsfh->offset, count, buf, cb, private_data, 1); } @@ -1867,6 +2050,7 @@ int nfs_close_async(struct nfs_context *nfs, struct nfsfh *nfsfh, nfs_cb cb, voi free(nfsfh->fh.data.data_val); nfsfh->fh.data.data_val = NULL; } + free(nfsfh->ra.buf); free(nfsfh); cb(0, nfs, NULL, private_data); @@ -3162,11 +3346,11 @@ static void nfs_statvfs_1_cb(struct rpc_context *rpc, int status, void *command_ return; } - svfs.f_bsize = 4096; - svfs.f_frsize = 4096; - svfs.f_blocks = res->FSSTAT3res_u.resok.tbytes/4096; - svfs.f_bfree = res->FSSTAT3res_u.resok.fbytes/4096; - svfs.f_bavail = res->FSSTAT3res_u.resok.abytes/4096; + svfs.f_bsize = NFS_BLKSIZE; + svfs.f_frsize = NFS_BLKSIZE; + svfs.f_blocks = res->FSSTAT3res_u.resok.tbytes/NFS_BLKSIZE; + svfs.f_bfree = res->FSSTAT3res_u.resok.fbytes/NFS_BLKSIZE; + svfs.f_bavail = res->FSSTAT3res_u.resok.abytes/NFS_BLKSIZE; svfs.f_files = res->FSSTAT3res_u.resok.tfiles; svfs.f_ffree = res->FSSTAT3res_u.resok.ffiles; #if !defined(ANDROID) @@ -4204,6 +4388,10 @@ void nfs_set_gid(struct nfs_context *nfs, int gid) { rpc_set_gid(nfs->rpc, gid); } +void nfs_set_readahead(struct nfs_context *nfs, uint32_t v) { + rpc_set_readahead(nfs->rpc, v); +} + void nfs_set_error(struct nfs_context *nfs, char *error_string, ...) { va_list ap;