X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=lib%2Flibnfs.c;h=459ebd7bf80e3e18e73dd24087a5653051dd6b81;hb=8dea7c3e2dce362f7a49a2193881a3e291f2fdbe;hp=30719ab562809cb59172cf5cd3b6b2434d0bffa3;hpb=390ff38ad1689f0708cdb18811b3b7d5d616cba7;p=deb_libnfs.git diff --git a/lib/libnfs.c b/lib/libnfs.c index 30719ab..459ebd7 100644 --- a/lib/libnfs.c +++ b/lib/libnfs.c @@ -77,7 +77,7 @@ #include "libnfs-raw-portmap.h" #include "libnfs-private.h" -#define MAX_DIR_CACHE 1024 +#define MAX_DIR_CACHE 128 struct nfsdir { struct nfs_fh3 fh; @@ -88,11 +88,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 { @@ -173,9 +184,8 @@ struct nfs_cb_data { int cancel; int oom; int num_calls; - uint64_t start_offset, max_offset, count; + uint64_t offset, count, max_offset, org_offset, org_count; char *buffer; - size_t request_size; char *usrbuf; }; @@ -220,12 +230,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; } @@ -759,6 +771,9 @@ static void nfs_mount_8_cb(struct rpc_context *rpc, int status, void *command_da return; } + /* NFS TCP connections we want to autoreconnect after sessions are torn down (due to inactivity or error) */ + rpc_set_autoreconnect(rpc); + args.fsroot = nfs->rootfh; if (rpc_nfs3_fsinfo_async(rpc, nfs_mount_9_cb, &args, data) != 0) { data->cb(-ENOMEM, nfs, command_data, data->private_data); @@ -811,9 +826,6 @@ static void nfs_mount_6_cb(struct rpc_context *rpc, int status, void *command_da free_nfs_cb_data(data); return; } - - /* NFS TCP connections we want to autoreconnect after sessions are torn down (due to inactivity or error) */ - rpc_set_autoreconnect(rpc); } @@ -1237,8 +1249,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; @@ -1621,9 +1633,9 @@ static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_dat /* if we have more than one call or we have received a short read we need a reassembly buffer */ 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; } } @@ -1632,7 +1644,7 @@ static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_dat 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, count); + memcpy(&data->buffer[mdata->offset - data->offset], res->READ3res_u.resok.data.data_val, count); } if (data->max_offset < mdata->offset + count) { data->max_offset = mdata->offset + count; @@ -1688,14 +1700,35 @@ static void nfs_pread_mcb(struct rpc_context *rpc, int status, void *command_dat } 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); } +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; @@ -1710,16 +1743,78 @@ static int nfs_pread_async_internal(struct nfs_context *nfs, struct nfsfh *nfsfh data->cb = cb; data->private_data = private_data; data->nfsfh = nfsfh; - data->request_size = count; + 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; @@ -1838,7 +1933,7 @@ static void nfs_pwrite_mcb(struct rpc_context *rpc, int status, void *command_da 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; @@ -1878,7 +1973,7 @@ static void nfs_pwrite_mcb(struct rpc_context *rpc, int status, void *command_da return; } - 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); } @@ -1907,7 +2002,7 @@ static int nfs_pwrite_async_internal(struct nfs_context *nfs, struct nfsfh *nfsf * 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; @@ -1934,7 +2029,7 @@ static int nfs_pwrite_async_internal(struct nfs_context *nfs, struct nfsfh *nfsf 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); @@ -2000,6 +2095,7 @@ static void nfs_write_append_cb(struct rpc_context *rpc, int status, void *comma int nfs_write_async(struct nfs_context *nfs, struct nfsfh *nfsfh, uint64_t count, char *buf, nfs_cb cb, void *private_data) { + nfs_ra_invalidate(nfsfh); if (nfsfh->is_append) { struct GETATTR3args args; struct nfs_cb_data *data; @@ -2043,6 +2139,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); @@ -2430,13 +2527,28 @@ int nfs_rmdir_async(struct nfs_context *nfs, const char *path, nfs_cb cb, void * /* * Async creat() */ +struct create_cb_data { + char *path; + int flags; + int mode; +}; + +static void free_create_cb_data(void *ptr) +{ + struct create_cb_data *data = ptr; + + free(data->path); + free(data); +} + static void nfs_create_2_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { LOOKUP3res *res; struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; struct nfsfh *nfsfh; - char *str = data->continue_data; + struct create_cb_data *cb_data = data->continue_data; + char *str = cb_data->path; assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -2469,6 +2581,13 @@ static void nfs_create_2_cb(struct rpc_context *rpc, int status, void *command_d } memset(nfsfh, 0, sizeof(struct nfsfh)); + if (cb_data->flags & O_SYNC) { + nfsfh->is_sync = 1; + } + if (cb_data->flags & O_APPEND) { + nfsfh->is_append = 1; + } + /* copy the filehandle */ nfsfh->fh.data.data_len = res->LOOKUP3res_u.resok.object.data.data_len; nfsfh->fh.data.data_val = malloc(nfsfh->fh.data.data_len); @@ -2480,12 +2599,13 @@ static void nfs_create_2_cb(struct rpc_context *rpc, int status, void *command_d -static void nfs_creat_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) +static void nfs_create_1_cb(struct rpc_context *rpc, int status, void *command_data, void *private_data) { CREATE3res *res; struct nfs_cb_data *data = private_data; struct nfs_context *nfs = data->nfs; - char *str = data->continue_data; + struct create_cb_data *cb_data = data->continue_data; + char *str = cb_data->path; LOOKUP3args args; assert(rpc->magic == RPC_CONTEXT_MAGIC); @@ -2523,9 +2643,10 @@ static void nfs_creat_1_cb(struct rpc_context *rpc, int status, void *command_da return; } -static int nfs_creat_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) +static int nfs_create_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_, struct nfs_cb_data *data) { - char *str = data->continue_data; + struct create_cb_data *cb_data = data->continue_data; + char *str = cb_data->path; CREATE3args args; str = &str[strlen(str) + 1]; @@ -2533,11 +2654,11 @@ static int nfs_creat_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_ memset(&args, 0, sizeof(CREATE3args)); args.where.dir = data->fh; args.where.name = str; - args.how.mode = UNCHECKED; + args.how.mode = (cb_data->flags & O_EXCL) ? GUARDED : UNCHECKED; args.how.createhow3_u.obj_attributes.mode.set_it = 1; - args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode = data->continue_int; + args.how.createhow3_u.obj_attributes.mode.set_mode3_u.mode = cb_data->mode; - if (rpc_nfs3_create_async(nfs->rpc, nfs_creat_1_cb, &args, data) != 0) { + if (rpc_nfs3_create_async(nfs->rpc, nfs_create_1_cb, &args, data) != 0) { rpc_set_error(nfs->rpc, "RPC error: Failed to send CREATE call for %s/%s", data->path, str); data->cb(-ENOMEM, nfs, rpc_get_error(nfs->rpc), data->private_data); free_nfs_cb_data(data); @@ -2546,27 +2667,37 @@ static int nfs_creat_continue_internal(struct nfs_context *nfs, fattr3 *attr _U_ return 0; } -int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data) +int nfs_create_async(struct nfs_context *nfs, const char *path, int flags, int mode, nfs_cb cb, void *private_data) { - char *new_path; + struct create_cb_data *cb_data; char *ptr; - new_path = strdup(path); - if (new_path == NULL) { + cb_data = malloc(sizeof(struct create_cb_data)); + if (cb_data == NULL) { + rpc_set_error(nfs->rpc, "Out of memory, failed to allocate mode buffer for cb data"); + return -1; + } + + cb_data->path = strdup(path); + if (cb_data->path == NULL) { rpc_set_error(nfs->rpc, "Out of memory, failed to allocate mode buffer for path"); + free(cb_data); return -1; } - ptr = strrchr(new_path, '/'); + ptr = strrchr(cb_data->path, '/'); if (ptr == NULL) { rpc_set_error(nfs->rpc, "Invalid path %s", path); - free(new_path); + free_create_cb_data(cb_data); return -1; } *ptr = 0; + cb_data->flags = flags; + cb_data->mode = mode; + /* new_path now points to the parent directory, and beyond the nul terminator is the new directory to create */ - if (nfs_lookuppath_async(nfs, new_path, cb, private_data, nfs_creat_continue_internal, new_path, free, mode) != 0) { + if (nfs_lookuppath_async(nfs, cb_data->path, cb, private_data, nfs_create_continue_internal, cb_data, free_create_cb_data, 0) != 0) { rpc_set_error(nfs->rpc, "Out of memory: failed to start parsing the path components"); return -1; } @@ -2574,6 +2705,11 @@ int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb return 0; } +int nfs_creat_async(struct nfs_context *nfs, const char *path, int mode, nfs_cb cb, void *private_data) +{ + return nfs_create_async(nfs, path, 0, mode, cb, private_data); +} + @@ -3371,11 +3507,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) @@ -4413,6 +4549,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;