aboutsummaryrefslogtreecommitdiff
path: root/hw/9pfs
diff options
context:
space:
mode:
Diffstat (limited to 'hw/9pfs')
-rw-r--r--hw/9pfs/9p-local.c49
-rw-r--r--hw/9pfs/9p-synth.c22
-rw-r--r--hw/9pfs/9p-util.h1
-rw-r--r--hw/9pfs/9p.c68
-rw-r--r--hw/9pfs/9p.h1
-rw-r--r--hw/9pfs/codir.c7
-rw-r--r--hw/9pfs/cofile.c7
-rw-r--r--hw/9pfs/cofs.c37
-rw-r--r--hw/9pfs/coth.h4
9 files changed, 167 insertions, 29 deletions
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
index 928523a..31e2162 100644
--- a/hw/9pfs/9p-local.c
+++ b/hw/9pfs/9p-local.c
@@ -766,16 +766,19 @@ out:
return err;
}
-static int local_fstat(FsContext *fs_ctx, int fid_type,
- V9fsFidOpenState *fs, struct stat *stbuf)
+static int local_fid_fd(int fid_type, V9fsFidOpenState *fs)
{
- int err, fd;
-
if (fid_type == P9_FID_DIR) {
- fd = dirfd(fs->dir.stream);
+ return dirfd(fs->dir.stream);
} else {
- fd = fs->fd;
+ return fs->fd;
}
+}
+
+static int local_fstat(FsContext *fs_ctx, int fid_type,
+ V9fsFidOpenState *fs, struct stat *stbuf)
+{
+ int err, fd = local_fid_fd(fid_type, fs);
err = fstat(fd, stbuf);
if (err) {
@@ -1039,6 +1042,14 @@ static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
return ret;
}
+static int local_ftruncate(FsContext *ctx, int fid_type, V9fsFidOpenState *fs,
+ off_t size)
+{
+ int fd = local_fid_fd(fid_type, fs);
+
+ return ftruncate(fd, size);
+}
+
static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
{
char *dirpath = g_path_get_dirname(fs_path->data);
@@ -1089,6 +1100,14 @@ out:
return ret;
}
+static int local_futimens(FsContext *s, int fid_type, V9fsFidOpenState *fs,
+ const struct timespec *times)
+{
+ int fd = local_fid_fd(fid_type, fs);
+
+ return qemu_futimens(fd, times);
+}
+
static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
int flags)
{
@@ -1167,13 +1186,7 @@ out:
static int local_fsync(FsContext *ctx, int fid_type,
V9fsFidOpenState *fs, int datasync)
{
- int fd;
-
- if (fid_type == P9_FID_DIR) {
- fd = dirfd(fs->dir.stream);
- } else {
- fd = fs->fd;
- }
+ int fd = local_fid_fd(fid_type, fs);
if (datasync) {
return qemu_fdatasync(fd);
@@ -1575,6 +1588,13 @@ static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp)
return 0;
}
+static bool local_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs)
+{
+ return
+ (fid_type == P9_FID_FILE && fs->fd != -1) ||
+ (fid_type == P9_FID_DIR && fs->dir.stream != NULL);
+}
+
FileOperations local_ops = {
.parse_opts = local_parse_opts,
.init = local_init,
@@ -1612,4 +1632,7 @@ FileOperations local_ops = {
.name_to_path = local_name_to_path,
.renameat = local_renameat,
.unlinkat = local_unlinkat,
+ .has_valid_file_handle = local_has_valid_file_handle,
+ .ftruncate = local_ftruncate,
+ .futimens = local_futimens,
};
diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c
index 2abaf3a..9cd1884 100644
--- a/hw/9pfs/9p-synth.c
+++ b/hw/9pfs/9p-synth.c
@@ -356,6 +356,13 @@ static int synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset)
return -1;
}
+static int synth_ftruncate(FsContext *ctx, int fid_type, V9fsFidOpenState *fs,
+ off_t size)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
static int synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
{
errno = EPERM;
@@ -417,6 +424,13 @@ static int synth_utimensat(FsContext *fs_ctx, V9fsPath *path,
return 0;
}
+static int synth_futimens(FsContext *fs_ctx, int fid_type, V9fsFidOpenState *fs,
+ const struct timespec *buf)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
static int synth_remove(FsContext *ctx, const char *path)
{
errno = EPERM;
@@ -615,6 +629,11 @@ static int synth_init(FsContext *ctx, Error **errp)
return 0;
}
+static bool synth_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs)
+{
+ return false;
+}
+
FileOperations synth_ops = {
.init = synth_init,
.lstat = synth_lstat,
@@ -650,4 +669,7 @@ FileOperations synth_ops = {
.name_to_path = synth_name_to_path,
.renameat = synth_renameat,
.unlinkat = synth_unlinkat,
+ .has_valid_file_handle = synth_has_valid_file_handle,
+ .ftruncate = synth_ftruncate,
+ .futimens = synth_futimens,
};
diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
index 7bc4ec8..a1924fe 100644
--- a/hw/9pfs/9p-util.h
+++ b/hw/9pfs/9p-util.h
@@ -103,6 +103,7 @@ static inline int errno_to_dotl(int err) {
#define qemu_renameat renameat
#define qemu_utimensat utimensat
#define qemu_unlinkat unlinkat
+#define qemu_futimens futimens
static inline void close_preserve_errno(int fd)
{
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
index 7cad2bc..8b001b9 100644
--- a/hw/9pfs/9p.c
+++ b/hw/9pfs/9p.c
@@ -434,16 +434,24 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu)
V9fsFidState *f;
GHashTableIter iter;
gpointer fid;
+ int err;
+ int nclosed = 0;
+
+ /* prevent multiple coroutines running this function simultaniously */
+ if (s->reclaiming) {
+ return;
+ }
+ s->reclaiming = true;
g_hash_table_iter_init(&iter, s->fids);
QSLIST_HEAD(, V9fsFidState) reclaim_list =
QSLIST_HEAD_INITIALIZER(reclaim_list);
+ /* Pick FIDs to be closed, collect them on reclaim_list. */
while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &f)) {
/*
- * Unlink fids cannot be reclaimed. Check
- * for them and skip them. Also skip fids
+ * Unlinked fids cannot be reclaimed, skip those, and also skip fids
* currently being operated on.
*/
if (f->ref || f->flags & FID_NON_RECLAIMABLE) {
@@ -493,23 +501,42 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu)
}
}
/*
- * Now close the fid in reclaim list. Free them if they
- * are already clunked.
+ * Close the picked FIDs altogether on a background I/O driver thread. Do
+ * this all at once to keep latency (i.e. amount of thread hops between main
+ * thread <-> fs driver background thread) as low as possible.
*/
+ v9fs_co_run_in_worker({
+ QSLIST_FOREACH(f, &reclaim_list, reclaim_next) {
+ err = (f->fid_type == P9_FID_DIR) ?
+ s->ops->closedir(&s->ctx, &f->fs_reclaim) :
+ s->ops->close(&s->ctx, &f->fs_reclaim);
+
+ /* 'man 2 close' suggests to ignore close() errors except of EBADF */
+ if (unlikely(err && errno == EBADF)) {
+ /*
+ * unexpected case as FIDs were picked above by having a valid
+ * file descriptor
+ */
+ error_report("9pfs: v9fs_reclaim_fd() WARNING: close() failed with EBADF");
+ } else {
+ /* total_open_fd must only be mutated on main thread */
+ nclosed++;
+ }
+ }
+ });
+ total_open_fd -= nclosed;
+ /* Free the closed FIDs. */
while (!QSLIST_EMPTY(&reclaim_list)) {
f = QSLIST_FIRST(&reclaim_list);
QSLIST_REMOVE(&reclaim_list, f, V9fsFidState, reclaim_next);
- if (f->fid_type == P9_FID_FILE) {
- v9fs_co_close(pdu, &f->fs_reclaim);
- } else if (f->fid_type == P9_FID_DIR) {
- v9fs_co_closedir(pdu, &f->fs_reclaim);
- }
/*
* Now drop the fid reference, free it
* if clunked.
*/
put_fid(pdu, f);
}
+
+ s->reclaiming = false;
}
/*
@@ -1574,6 +1601,11 @@ out_nofid:
pdu_complete(pdu, err);
}
+static bool fid_has_valid_file_handle(V9fsState *s, V9fsFidState *fidp)
+{
+ return s->ops->has_valid_file_handle(fidp->fid_type, &fidp->fs);
+}
+
static void coroutine_fn v9fs_getattr(void *opaque)
{
int32_t fid;
@@ -1596,9 +1628,7 @@ static void coroutine_fn v9fs_getattr(void *opaque)
retval = -ENOENT;
goto out_nofid;
}
- if ((fidp->fid_type == P9_FID_FILE && fidp->fs.fd != -1) ||
- (fidp->fid_type == P9_FID_DIR && fidp->fs.dir.stream))
- {
+ if (fid_has_valid_file_handle(pdu->s, fidp)) {
retval = v9fs_co_fstat(pdu, fidp, &stbuf);
} else {
retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
@@ -1705,7 +1735,11 @@ static void coroutine_fn v9fs_setattr(void *opaque)
} else {
times[1].tv_nsec = UTIME_OMIT;
}
- err = v9fs_co_utimensat(pdu, &fidp->path, times);
+ if (fid_has_valid_file_handle(pdu->s, fidp)) {
+ err = v9fs_co_futimens(pdu, fidp, times);
+ } else {
+ err = v9fs_co_utimensat(pdu, &fidp->path, times);
+ }
if (err < 0) {
goto out;
}
@@ -1730,7 +1764,11 @@ static void coroutine_fn v9fs_setattr(void *opaque)
}
}
if (v9iattr.valid & (P9_ATTR_SIZE)) {
- err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size);
+ if (fid_has_valid_file_handle(pdu->s, fidp)) {
+ err = v9fs_co_ftruncate(pdu, fidp, v9iattr.size);
+ } else {
+ err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size);
+ }
if (err < 0) {
goto out;
}
@@ -4324,6 +4362,8 @@ int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t,
s->ctx.fst = &fse->fst;
fsdev_throttle_init(s->ctx.fst);
+ s->reclaiming = false;
+
rc = 0;
out:
if (rc) {
diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h
index 5e041e1..259ad32 100644
--- a/hw/9pfs/9p.h
+++ b/hw/9pfs/9p.h
@@ -362,6 +362,7 @@ struct V9fsState {
uint64_t qp_ndevices; /* Amount of entries in qpd_table. */
uint16_t qp_affix_next;
uint64_t qp_fullpath_next;
+ bool reclaiming;
};
/* 9p2000.L open flags */
diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c
index 2068a47..bce7dd9 100644
--- a/hw/9pfs/codir.c
+++ b/hw/9pfs/codir.c
@@ -20,6 +20,7 @@
#include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h"
#include "qemu/main-loop.h"
+#include "qemu/error-report.h"
#include "coth.h"
#include "9p-xattr.h"
#include "9p-util.h"
@@ -353,7 +354,11 @@ int coroutine_fn v9fs_co_closedir(V9fsPDU *pdu, V9fsFidOpenState *fs)
err = -errno;
}
});
- if (!err) {
+ /* 'man 2 close' suggests to ignore close() errors except of EBADF */
+ if (unlikely(err && errno == EBADF)) {
+ /* unexpected case as we should have checked for a valid file handle */
+ error_report("9pfs: WARNING: v9fs_co_closedir() failed with EBADF");
+ } else {
total_open_fd--;
}
return err;
diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c
index 71174c3..6e775c8 100644
--- a/hw/9pfs/cofile.c
+++ b/hw/9pfs/cofile.c
@@ -20,6 +20,7 @@
#include "fsdev/qemu-fsdev.h"
#include "qemu/thread.h"
#include "qemu/main-loop.h"
+#include "qemu/error-report.h"
#include "coth.h"
int coroutine_fn v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode,
@@ -197,7 +198,11 @@ int coroutine_fn v9fs_co_close(V9fsPDU *pdu, V9fsFidOpenState *fs)
err = -errno;
}
});
- if (!err) {
+ /* 'man 2 close' suggests to ignore close() errors except of EBADF */
+ if (unlikely(err && errno == EBADF)) {
+ /* unexpected case as we should have checked for a valid file handle */
+ error_report("9pfs: WARNING: v9fs_co_close() failed with EBADF");
+ } else {
total_open_fd--;
}
return err;
diff --git a/hw/9pfs/cofs.c b/hw/9pfs/cofs.c
index 67e3ae5..12fa8c9 100644
--- a/hw/9pfs/cofs.c
+++ b/hw/9pfs/cofs.c
@@ -139,6 +139,25 @@ int coroutine_fn v9fs_co_utimensat(V9fsPDU *pdu, V9fsPath *path,
return err;
}
+int coroutine_fn v9fs_co_futimens(V9fsPDU *pdu, V9fsFidState *fidp,
+ struct timespec times[2])
+{
+ int err;
+ V9fsState *s = pdu->s;
+
+ if (v9fs_request_cancelled(pdu)) {
+ return -EINTR;
+ }
+ v9fs_co_run_in_worker(
+ {
+ err = s->ops->futimens(&s->ctx, fidp->fid_type, &fidp->fs, times);
+ if (err < 0) {
+ err = -errno;
+ }
+ });
+ return err;
+}
+
int coroutine_fn v9fs_co_chown(V9fsPDU *pdu, V9fsPath *path, uid_t uid,
gid_t gid)
{
@@ -184,6 +203,24 @@ int coroutine_fn v9fs_co_truncate(V9fsPDU *pdu, V9fsPath *path, off_t size)
return err;
}
+int coroutine_fn v9fs_co_ftruncate(V9fsPDU *pdu, V9fsFidState *fidp, off_t size)
+{
+ int err;
+ V9fsState *s = pdu->s;
+
+ if (v9fs_request_cancelled(pdu)) {
+ return -EINTR;
+ }
+ v9fs_co_run_in_worker(
+ {
+ err = s->ops->ftruncate(&s->ctx, fidp->fid_type, &fidp->fs, size);
+ if (err < 0) {
+ err = -errno;
+ }
+ });
+ return err;
+}
+
int coroutine_fn v9fs_co_mknod(V9fsPDU *pdu, V9fsFidState *fidp,
V9fsString *name, uid_t uid, gid_t gid,
dev_t dev, mode_t mode, struct stat *stbuf)
diff --git a/hw/9pfs/coth.h b/hw/9pfs/coth.h
index 2c54249..7906fa7 100644
--- a/hw/9pfs/coth.h
+++ b/hw/9pfs/coth.h
@@ -71,8 +71,12 @@ int coroutine_fn v9fs_co_statfs(V9fsPDU *, V9fsPath *, struct statfs *);
int coroutine_fn v9fs_co_lstat(V9fsPDU *, V9fsPath *, struct stat *);
int coroutine_fn v9fs_co_chmod(V9fsPDU *, V9fsPath *, mode_t);
int coroutine_fn v9fs_co_utimensat(V9fsPDU *, V9fsPath *, struct timespec [2]);
+int coroutine_fn v9fs_co_futimens(V9fsPDU *pdu, V9fsFidState *fidp,
+ struct timespec times[2]);
int coroutine_fn v9fs_co_chown(V9fsPDU *, V9fsPath *, uid_t, gid_t);
int coroutine_fn v9fs_co_truncate(V9fsPDU *, V9fsPath *, off_t);
+int coroutine_fn v9fs_co_ftruncate(V9fsPDU *pdu, V9fsFidState *fidp,
+ off_t size);
int coroutine_fn v9fs_co_llistxattr(V9fsPDU *, V9fsPath *, void *, size_t);
int coroutine_fn v9fs_co_lgetxattr(V9fsPDU *, V9fsPath *,
V9fsString *, void *, size_t);