diff --git a/fsdev/9p-marshal.c b/fsdev/9p-marshal.c index a01bba6908..51881fe220 100644 --- a/fsdev/9p-marshal.c +++ b/fsdev/9p-marshal.c @@ -18,6 +18,8 @@ #include "9p-marshal.h" +P9ARRAY_DEFINE_TYPE(V9fsString, v9fs_string_free); + void v9fs_string_free(V9fsString *str) { g_free(str->data); diff --git a/fsdev/9p-marshal.h b/fsdev/9p-marshal.h index ceaf2f521e..f1abbe151c 100644 --- a/fsdev/9p-marshal.h +++ b/fsdev/9p-marshal.h @@ -1,10 +1,13 @@ #ifndef QEMU_9P_MARSHAL_H #define QEMU_9P_MARSHAL_H +#include "p9array.h" + typedef struct V9fsString { uint16_t size; char *data; } V9fsString; +P9ARRAY_DECLARE_TYPE(V9fsString); typedef struct V9fsQID { uint8_t type; diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h index 42f677cf38..8fd89f0447 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h @@ -18,6 +18,7 @@ #include #include #include "qemu-fsdev-throttle.h" +#include "p9array.h" #define SM_LOCAL_MODE_BITS 0600 #define SM_LOCAL_DIR_MODE_BITS 0700 @@ -105,6 +106,7 @@ struct V9fsPath { uint16_t size; char *data; }; +P9ARRAY_DECLARE_TYPE(V9fsPath); typedef union V9fsFidOpenState V9fsFidOpenState; diff --git a/fsdev/p9array.h b/fsdev/p9array.h new file mode 100644 index 0000000000..6aa25327ca --- /dev/null +++ b/fsdev/p9array.h @@ -0,0 +1,160 @@ +/* + * P9Array - deep auto free C-array + * + * Copyright (c) 2021 Crudebyte + * + * Authors: + * Christian Schoenebeck + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_P9ARRAY_H +#define QEMU_P9ARRAY_H + +#include "qemu/compiler.h" + +/** + * P9Array provides a mechanism to access arrays in common C-style (e.g. by + * square bracket [] operator) in conjunction with reference variables that + * perform deep auto free of the array when leaving the scope of the auto + * reference variable. That means not only is the array itself automatically + * freed, but also memory dynamically allocated by the individual array + * elements. + * + * Example: + * + * Consider the following user struct @c Foo which shall be used as scalar + * (element) type of an array: + * @code + * typedef struct Foo { + * int i; + * char *s; + * } Foo; + * @endcode + * and assume it has the following function to free memory allocated by @c Foo + * instances: + * @code + * void free_foo(Foo *foo) { + * free(foo->s); + * } + * @endcode + * Add the following to a shared header file: + * @code + * P9ARRAY_DECLARE_TYPE(Foo); + * @endcode + * and the following to a C unit file: + * @code + * P9ARRAY_DEFINE_TYPE(Foo, free_foo); + * @endcode + * Finally the array may then be used like this: + * @code + * void doSomething(size_t n) { + * P9ARRAY_REF(Foo) foos = NULL; + * P9ARRAY_NEW(Foo, foos, n); + * for (size_t i = 0; i < n; ++i) { + * foos[i].i = i; + * foos[i].s = calloc(4096, 1); + * snprintf(foos[i].s, 4096, "foo %d", i); + * if (...) { + * return; // array auto freed here + * } + * } + * // array auto freed here + * } + * @endcode + */ + +/** + * Declares an array type for the passed @a scalar_type. + * + * This is typically used from a shared header file. + * + * @param scalar_type - type of the individual array elements + */ +#define P9ARRAY_DECLARE_TYPE(scalar_type) \ + typedef struct P9Array##scalar_type { \ + size_t len; \ + scalar_type first[]; \ + } P9Array##scalar_type; \ + \ + void p9array_new_##scalar_type(scalar_type **auto_var, size_t len); \ + void p9array_auto_free_##scalar_type(scalar_type **auto_var); \ + +/** + * Defines an array type for the passed @a scalar_type and appropriate + * @a scalar_cleanup_func. + * + * This is typically used from a C unit file. + * + * @param scalar_type - type of the individual array elements + * @param scalar_cleanup_func - appropriate function to free memory dynamically + * allocated by individual array elements before + */ +#define P9ARRAY_DEFINE_TYPE(scalar_type, scalar_cleanup_func) \ + void p9array_new_##scalar_type(scalar_type **auto_var, size_t len) \ + { \ + p9array_auto_free_##scalar_type(auto_var); \ + P9Array##scalar_type *arr = g_malloc0(sizeof(P9Array##scalar_type) + \ + len * sizeof(scalar_type)); \ + arr->len = len; \ + *auto_var = &arr->first[0]; \ + } \ + \ + void p9array_auto_free_##scalar_type(scalar_type **auto_var) \ + { \ + scalar_type *first = (*auto_var); \ + if (!first) { \ + return; \ + } \ + P9Array##scalar_type *arr = (P9Array##scalar_type *) ( \ + ((char *)first) - offsetof(P9Array##scalar_type, first) \ + ); \ + for (size_t i = 0; i < arr->len; ++i) { \ + scalar_cleanup_func(&arr->first[i]); \ + } \ + g_free(arr); \ + } \ + +/** + * Used to declare a reference variable (unique pointer) for an array. After + * leaving the scope of the reference variable, the associated array is + * automatically freed. + * + * @param scalar_type - type of the individual array elements + */ +#define P9ARRAY_REF(scalar_type) \ + __attribute((__cleanup__(p9array_auto_free_##scalar_type))) scalar_type* + +/** + * Allocates a new array of passed @a scalar_type with @a len number of array + * elements and assigns the created array to the reference variable + * @a auto_var. + * + * @param scalar_type - type of the individual array elements + * @param auto_var - destination reference variable + * @param len - amount of array elements to be allocated immediately + */ +#define P9ARRAY_NEW(scalar_type, auto_var, len) \ + QEMU_BUILD_BUG_MSG( \ + !__builtin_types_compatible_p(scalar_type, typeof(*auto_var)), \ + "P9Array scalar type mismatch" \ + ); \ + p9array_new_##scalar_type((&auto_var), len) + +#endif /* QEMU_P9ARRAY_H */ diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index c857b31321..15b3f4d385 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -50,6 +50,8 @@ enum { Oappend = 0x80, }; +P9ARRAY_DEFINE_TYPE(V9fsPath, v9fs_path_free); + static ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) { ssize_t ret; @@ -1262,6 +1264,37 @@ static int coroutine_fn stat_to_v9stat(V9fsPDU *pdu, V9fsPath *path, #define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */ +/** + * Convert host filesystem's block size into an appropriate block size for + * 9p client (guest OS side). The value returned suggests an "optimum" block + * size for 9p I/O, i.e. to maximize performance. + * + * @pdu: 9p client request + * @blksize: host filesystem's block size + */ +static int32_t blksize_to_iounit(const V9fsPDU *pdu, int32_t blksize) +{ + int32_t iounit = 0; + V9fsState *s = pdu->s; + + /* + * iounit should be multiples of blksize (host filesystem block size) + * as well as less than (client msize - P9_IOHDRSZ) + */ + if (blksize) { + iounit = QEMU_ALIGN_DOWN(s->msize - P9_IOHDRSZ, blksize); + } + if (!iounit) { + iounit = s->msize - P9_IOHDRSZ; + } + return iounit; +} + +static int32_t stat_to_iounit(const V9fsPDU *pdu, const struct stat *stbuf) +{ + return blksize_to_iounit(pdu, stbuf->st_blksize); +} + static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, V9fsStatDotl *v9lstat) { @@ -1273,7 +1306,7 @@ static int stat_to_v9stat_dotl(V9fsPDU *pdu, const struct stat *stbuf, v9lstat->st_gid = stbuf->st_gid; v9lstat->st_rdev = stbuf->st_rdev; v9lstat->st_size = stbuf->st_size; - v9lstat->st_blksize = stbuf->st_blksize; + v9lstat->st_blksize = stat_to_iounit(pdu, stbuf); v9lstat->st_blocks = stbuf->st_blocks; v9lstat->st_atime_sec = stbuf->st_atime; v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec; @@ -1705,13 +1738,14 @@ static void coroutine_fn v9fs_walk(void *opaque) int name_idx; g_autofree V9fsQID *qids = NULL; int i, err = 0; - V9fsPath dpath, path, *pathes = NULL; + V9fsPath dpath, path; + P9ARRAY_REF(V9fsPath) pathes = NULL; uint16_t nwnames; struct stat stbuf, fidst; g_autofree struct stat *stbufs = NULL; size_t offset = 7; int32_t fid, newfid; - V9fsString *wnames = NULL; + P9ARRAY_REF(V9fsString) wnames = NULL; V9fsFidState *fidp; V9fsFidState *newfidp = NULL; V9fsPDU *pdu = opaque; @@ -1732,10 +1766,10 @@ static void coroutine_fn v9fs_walk(void *opaque) goto out_nofid; } if (nwnames) { - wnames = g_new0(V9fsString, nwnames); + P9ARRAY_NEW(V9fsString, wnames, nwnames); qids = g_new0(V9fsQID, nwnames); stbufs = g_new0(struct stat, nwnames); - pathes = g_new0(V9fsPath, nwnames); + P9ARRAY_NEW(V9fsPath, pathes, nwnames); for (i = 0; i < nwnames; i++) { err = pdu_unmarshal(pdu, offset, "s", &wnames[i]); if (err < 0) { @@ -1867,36 +1901,14 @@ out: v9fs_path_free(&path); out_nofid: pdu_complete(pdu, err); - if (nwnames && nwnames <= P9_MAXWELEM) { - for (name_idx = 0; name_idx < nwnames; name_idx++) { - v9fs_string_free(&wnames[name_idx]); - v9fs_path_free(&pathes[name_idx]); - } - g_free(wnames); - g_free(pathes); - } } static int32_t coroutine_fn get_iounit(V9fsPDU *pdu, V9fsPath *path) { struct statfs stbuf; - int32_t iounit = 0; - V9fsState *s = pdu->s; + int err = v9fs_co_statfs(pdu, path, &stbuf); - /* - * iounit should be multiples of f_bsize (host filesystem block size - * and as well as less than (client msize - P9_IOHDRSZ)) - */ - if (!v9fs_co_statfs(pdu, path, &stbuf)) { - if (stbuf.f_bsize) { - iounit = stbuf.f_bsize; - iounit *= (s->msize - P9_IOHDRSZ) / stbuf.f_bsize; - } - } - if (!iounit) { - iounit = s->msize - P9_IOHDRSZ; - } - return iounit; + return blksize_to_iounit(pdu, (err >= 0) ? stbuf.f_bsize : 0); } static void coroutine_fn v9fs_open(void *opaque)