Files
qemu/fsdev/qemu-fsdev.c
Christian Schoenebeck e6116a81f0 hw/9pfs: add max_xattr option
Previous patch introduced a limit of max. 1024 simultaneous xattr FIDs.

This patch introduces an option "max_attr" that allows to override this
limit, just for the case that some user might run into this limit for
some reason, even if unlikely; or for reducing the limit further down
(e.g. that default limit of 1024 would cap at max. 64 MiB host memory,
at least on Linux hosts where the limit per xattr is 64k).

This new "max_xattr" option can be specified with both -fsdev and
-virtfs command line options, with the "local" and the "synth" fs
drivers.

The previous limit of 1024 is preserved as the default value.

Link: https://lore.kernel.org/qemu-devel/b7631ac0d8dde0629bc7c4f2c4185d9f57b962b4.1781361555.git.qemu_oss@crudebyte.com
Signed-off-by: Christian Schoenebeck <qemu_oss@crudebyte.com>
2026-06-29 15:10:31 +02:00

183 lines
4.5 KiB
C

/*
* 9p
*
* Copyright IBM, Corp. 2010
*
* Authors:
* Gautham R Shenoy <ego@in.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu-fsdev.h"
#include "qemu/queue.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
/*
* A table to store the various file systems and their callback operations.
* -----------------
* fstype | ops
* -----------------
* local | local_ops
* . |
* . |
* . |
* . |
* -----------------
* etc
*/
typedef struct FsDriverTable {
const char *name;
FileOperations *ops;
const char **opts;
} FsDriverTable;
typedef struct FsDriverListEntry {
FsDriverEntry fse;
QTAILQ_ENTRY(FsDriverListEntry) next;
} FsDriverListEntry;
static QTAILQ_HEAD(, FsDriverListEntry) fsdriver_entries =
QTAILQ_HEAD_INITIALIZER(fsdriver_entries);
#define COMMON_FS_DRIVER_OPTIONS "id", "fsdriver", "readonly", "max_xattr"
static FsDriverTable FsDrivers[] = {
{
.name = "local",
.ops = &local_ops,
.opts = (const char * []) {
COMMON_FS_DRIVER_OPTIONS,
"security_model",
"path",
"writeout",
"fmode",
"dmode",
"multidevs",
"throttling.bps-total",
"throttling.bps-read",
"throttling.bps-write",
"throttling.iops-total",
"throttling.iops-read",
"throttling.iops-write",
"throttling.bps-total-max",
"throttling.bps-read-max",
"throttling.bps-write-max",
"throttling.iops-total-max",
"throttling.iops-read-max",
"throttling.iops-write-max",
"throttling.bps-total-max-length",
"throttling.bps-read-max-length",
"throttling.bps-write-max-length",
"throttling.iops-total-max-length",
"throttling.iops-read-max-length",
"throttling.iops-write-max-length",
"throttling.iops-size",
NULL
},
},
{
.name = "synth",
.ops = &synth_ops,
.opts = (const char * []) {
COMMON_FS_DRIVER_OPTIONS,
NULL
},
},
};
static int validate_opt(void *opaque, const char *name, const char *value,
Error **errp)
{
FsDriverTable *drv = opaque;
const char **opt;
for (opt = drv->opts; *opt; opt++) {
if (!strcmp(*opt, name)) {
return 0;
}
}
error_setg(errp, "'%s' is invalid for fsdriver '%s'", name, drv->name);
return -1;
}
int qemu_fsdev_add(QemuOpts *opts, Error **errp)
{
int i;
struct FsDriverListEntry *fsle;
const char *fsdev_id = qemu_opts_id(opts);
const char *fsdriver = qemu_opt_get(opts, "fsdriver");
const char *writeout = qemu_opt_get(opts, "writeout");
bool ro = qemu_opt_get_bool(opts, "readonly", 0);
if (!fsdev_id) {
error_setg(errp, "fsdev: No id specified");
return -1;
}
if (fsdriver) {
for (i = 0; i < ARRAY_SIZE(FsDrivers); i++) {
if (strcmp(FsDrivers[i].name, fsdriver) == 0) {
break;
}
}
if (i == ARRAY_SIZE(FsDrivers)) {
error_setg(errp, "fsdev: fsdriver %s not found", fsdriver);
return -1;
}
} else {
error_setg(errp, "fsdev: No fsdriver specified");
return -1;
}
if (qemu_opt_foreach(opts, validate_opt, &FsDrivers[i], errp)) {
return -1;
}
fsle = g_malloc0(sizeof(*fsle));
fsle->fse.fsdev_id = g_strdup(fsdev_id);
fsle->fse.ops = FsDrivers[i].ops;
if (writeout) {
if (!strcmp(writeout, "immediate")) {
fsle->fse.export_flags |= V9FS_IMMEDIATE_WRITEOUT;
}
}
if (ro) {
fsle->fse.export_flags |= V9FS_RDONLY;
} else {
fsle->fse.export_flags &= ~V9FS_RDONLY;
}
if (fsle->fse.ops->parse_opts) {
if (fsle->fse.ops->parse_opts(opts, &fsle->fse, errp)) {
g_free(fsle->fse.fsdev_id);
g_free(fsle);
return -1;
}
}
QTAILQ_INSERT_TAIL(&fsdriver_entries, fsle, next);
return 0;
}
FsDriverEntry *get_fsdev_fsentry(char *id)
{
if (id) {
struct FsDriverListEntry *fsle;
QTAILQ_FOREACH(fsle, &fsdriver_entries, next) {
if (strcmp(fsle->fse.fsdev_id, id) == 0) {
return &fsle->fse;
}
}
}
return NULL;
}