From: Wolfgang Bumiller <w.bumiller@proxmox.com>
To: Dominik Csapak <d.csapak@proxmox.com>
Cc: pve-devel@lists.proxmox.com
Subject: Re: [pve-devel] [PATCH cluster v10 1/5] add CFS_IPC_GET_GUEST_CONFIG_PROPERTIES method
Date: Wed, 16 Nov 2022 10:50:38 +0100 [thread overview]
Message-ID: <20221116095038.vrvn43aeq33vrame@casey.proxmox.com> (raw)
In-Reply-To: <20221115130248.1007325-2-d.csapak@proxmox.com>
On Tue, Nov 15, 2022 at 02:02:26PM +0100, Dominik Csapak wrote:
> for getting multiple properties from the in memory config of the
> guests in one go. Keep the existing IPC call as is for backward
> compatibility and add this as separate, new one.
>
> It basically behaves the same as
> CFS_IPC_GET_GUEST_CONFIG_PROPERTY, but takes a list of properties
> instead and returns multiple properties per guest.
>
> The old way of getting a single property is now also done by
> the new function, since it basically performs identically.
>
> Here a short benchmark:
>
> Setup:
> PVE in a VM with cpu type host (12700k) and 4 cores
> 10000 typical configs with both 'lock' and 'tags' set at the end
> and fairly long tags ('test-tag1,test-tag2,test-tag3')
> (normal vm with a snapshot, ~1KiB)
>
> i let it run 100 times each, times in ms
>
> old code:
>
> total time time per iteration
> 1054.2 10.2
>
> new code:
>
> num props total time time per iter function
> 2 1332.2 13.2 get_properties
> 1 1051.2 10.2 get_properties
> 2 2082.2 20.2 get_property (2 calls to get_property)
> 1 1034.2 10.2 get_property
>
> so a call with the new code for one property is the same as with the
> old code, and adding a second property only adds a bit of additional
> time (in this case ~30%)
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
Sort-of
Acked-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
but I do have a bit of a maintainability-complaint and potential actual
issue (see below).
> changes from v9:
> * line_len, prop_len -> size_t
> * document min/max
> * remove setting of null byte in loop (it's already split at the null byte)
> * improved the loop for skipping whitespace at the beginning
> data/src/cfs-ipc-ops.h | 2 +
> data/src/server.c | 63 ++++++++++++++
> data/src/status.c | 184 ++++++++++++++++++++++++++++-------------
> data/src/status.h | 3 +
> 4 files changed, 194 insertions(+), 58 deletions(-)
>
> diff --git a/data/src/status.c b/data/src/status.c
> index 9bceaeb..47cb42e 100644
> --- a/data/src/status.c
> +++ b/data/src/status.c
> @@ -804,25 +804,55 @@ cfs_create_vmlist_msg(GString *str)
> return 0;
> }
>
> -// checks the conf for a line starting with '$prop:' and returns the value
> -// afterwards, whitout initial whitespace(s), we only deal with the format
> -// restricion imposed by our perl VM config parser, main reference is
> +// checks if a config line starts with the given prop. if yes, writes a '\0'
> +// at the end of the value, and returns the pointer where the value starts
Please add:
// `line[line_len]` is guaranteed to be `\0`
keep reading for why ;-)
> +char *
> +_get_property_value_from_line(char *line, size_t line_len, const char *prop, size_t prop_len)
> +{
> + if (line_len <= prop_len + 1) return NULL;
> +
> + if (line[prop_len] == ':' && memcmp(line, prop, prop_len) == 0) { // found
> + char *v_start = &line[prop_len + 1];
> + char *v_end = &line[line_len - 1];
nit: in most C code an start and end pointers are exclusive on the end side
(iow. end should point to the first invalid address) it's a bit
confusing doing it this way and may lead to confusion...
> +
> + // drop initial value whitespaces here already
> + while (v_end > v_start && *v_start && isspace(*v_start)) v_start++;
(((that's fine, but I find 'v_start < v_end' much easier to parse)))
> +
> + if (!*v_start) return NULL;
> +
this is where the v_end tripped me up a bit:
- if there's no value, v_start == v_end and the loop won't run
> + while (v_end > v_start && isspace(*v_end)) v_end--;
> + v_end[1] = '\0';
which makes this read as "write to 1 past the end"
which *seems* fine at first given that we split the lines by replacing
'\n' with '\0', but then there's another case to think about:
The original data just came from a `memdb_read()` which just gives us a
"binary blob" and may not be newline-terminated.
However, the *caller* guarantees that in this case, we simply ignore the
final line.
BUT: do we do the same in perl?
> +
> + return v_start;
> + }
> +
> + return NULL;
> +}
> +
> +// checks the conf for lines starting with the given props and
> +// writes the pointers into the correct positions into the 'found' array
> +// afterwards, without initial whitespace(s), we only deal with the format
> +// restriction imposed by our perl VM config parser, main reference is
> // PVE::QemuServer::parse_vm_config this allows to be very fast and still
> // relatively simple
> -// main restrictions used for our advantage is the properties match reges:
> +// main restrictions used for our advantage is the properties match regex:
> // ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) from parse_vm_config
> // currently we only look at the current configuration in place, i.e., *no*
> -// snapshort and *no* pending changes
> -static char *
> -_get_property_value(char *conf, int conf_size, const char *prop, int prop_len)
> +// snapshot and *no* pending changes
> +//
> +// min..max is the char range of the first character of the given props,
> +// so that we can return early when checking the line
> +void
> +_get_property_values(char **found, char *conf, int conf_size, const char **props, uint8_t num_props, char min, char max)
> {
> const char *const conf_end = conf + conf_size;
> char *line = conf;
> - size_t remaining_size;
> + size_t remaining_size = conf_size;
> + int count = 0;
^ I'd prefer an uint8_t here and call it `prop_count` as it's strictly
to count towards the above `uint8_t num_props`, so `int` makes little
sense, and the generic name `count` with type `uint8_t` is also a bit
awkward ;-)
>
> char *next_newline = memchr(conf, '\n', conf_size);
> if (next_newline == NULL) {
> - return NULL; // valid property lines end with \n, but none in the config
> + return; // valid property lines end with \n, but none in the config
> }
> *next_newline = '\0';
>
> @@ -830,41 +860,32 @@ _get_property_value(char *conf, int conf_size, const char *prop, int prop_len)
> if (!line[0]) goto next;
>
> // snapshot or pending section start, but nothing found yet -> not found
> - if (line[0] == '[') return NULL;
> - // properties start with /^[a-z]/, so continue early if not
> - if (line[0] < 'a' || line[0] > 'z') goto next;
> -
> - int line_len = strlen(line);
> - if (line_len <= prop_len + 1) goto next;
> -
> - if (line[prop_len] == ':' && memcmp(line, prop, prop_len) == 0) { // found
> - char *v_start = &line[prop_len + 1];
> -
> - // drop initial value whitespaces here already
> - while (*v_start && isspace(*v_start)) v_start++;
> -
> - if (!*v_start) return NULL;
> -
> - char *v_end = &line[line_len - 1];
> - while (v_end > v_start && isspace(*v_end)) v_end--;
> - v_end[1] = '\0';
> -
> - return v_start;
> + if (line[0] == '[') return;
> + // continue early if line does not begin with the min/max char of the properties
> + if (line[0] < min || line[0] > max) goto next;
> +
> + size_t line_len = strlen(line);
This is only fine because we skip the final line if it's not
`\n`-terminated (as it's otherwise not guaranteed to be `\0`-terminated)
But we don't even need to re-scan the line, since we already did that,
and should just be able to use:
line_len = next_newline - line;
no?
> + for (uint8_t i = 0; i < num_props; i++) {
> + char * value = _get_property_value_from_line(line, line_len, props[i], strlen(props[i]));
> + if (value != NULL) {
> + count += (found[i] != NULL) & 0x1; // count newly found lines
> + found[i] = value;
> + }
> + }
> + if (count == num_props) {
> + return; // found all
> }
> next:
> line = next_newline + 1;
> remaining_size = conf_end - line;
> - if (remaining_size <= prop_len) {
> - return NULL;
> - }
> next_newline = memchr(line, '\n', remaining_size);
> if (next_newline == NULL) {
> - return NULL; // valid property lines end with \n, but none in the config
> + return; // valid property lines end with \n, but none in the config
> }
> *next_newline = '\0';
> }
>
> - return NULL; // not found
> + return;
> }
>
> static void
next prev parent reply other threads:[~2022-11-16 9:50 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-11-15 13:02 [pve-devel] [PATCH cluster/guest-common/qemu-server/container/wt/manager v10 0/5] add tags to ui Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH cluster v10 1/5] add CFS_IPC_GET_GUEST_CONFIG_PROPERTIES method Dominik Csapak
2022-11-16 9:50 ` Wolfgang Bumiller [this message]
2022-11-15 13:02 ` [pve-devel] [PATCH cluster v10 2/5] Cluster: add get_guest_config_properties Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH cluster v10 3/5] datacenter.cfg: add option for tag-style Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH cluster v10 4/5] datacenter.cfg: add tag rights control to the datacenter config Dominik Csapak
2022-11-15 15:17 ` Fabian Grünbichler
2022-11-16 7:48 ` Thomas Lamprecht
2022-11-16 8:47 ` Dominik Csapak
2022-11-16 8:51 ` Fabian Grünbichler
2022-11-16 8:54 ` Thomas Lamprecht
2022-11-16 9:04 ` Dominik Csapak
2022-11-16 9:10 ` Thomas Lamprecht
2022-11-16 9:31 ` Fabian Grünbichler
2022-11-16 9:38 ` Dominik Csapak
2022-11-16 9:40 ` Thomas Lamprecht
2022-11-16 9:51 ` Fabian Grünbichler
2022-11-16 13:56 ` Thomas Lamprecht
2022-11-15 13:02 ` [pve-devel] [PATCH cluster v10 5/5] datacenter.cfg: add 'ordering' to 'tag-style' config Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH guest-common v10 1/1] GuestHelpers: add 'assert_tag_permissions' Dominik Csapak
2022-11-15 15:34 ` Fabian Grünbichler
2022-11-15 13:02 ` [pve-devel] [PATCH qemu-server v10 1/1] api: update: check for tags permissions with 'assert_tag_permissions' Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH container v10 1/1] check_ct_modify_config_perm: " Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH widget-toolkit v10 1/2] add tag related helpers Dominik Csapak
2022-11-16 13:48 ` [pve-devel] applied: " Thomas Lamprecht
2022-11-15 13:02 ` [pve-devel] [PATCH widget-toolkit v10 2/2] Toolkit: add override for Ext.dd.DragDropManager Dominik Csapak
2022-11-16 13:49 ` [pve-devel] applied: " Thomas Lamprecht
2022-11-15 13:02 ` [pve-devel] [PATCH manager v10 01/13] api: /cluster/resources: add tags to returned properties Dominik Csapak
2022-11-16 8:02 ` Thomas Lamprecht
2022-11-15 13:02 ` [pve-devel] [PATCH manager v10 02/13] api: add /ui-options api call Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH manager v10 03/13] ui: call '/ui-options' and save the result in PVE.UIOptions Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH manager v10 04/13] ui: parse and save tag infos from /ui-options Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH manager v10 05/13] ui: add form/TagColorGrid Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH manager v10 06/13] ui: add PVE.form.ListField Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH manager v10 07/13] ui: dc/OptionView: add editors for tag settings Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH manager v10 08/13] ui: add form/Tag Dominik Csapak
2022-11-16 14:57 ` Thomas Lamprecht
2022-11-15 13:02 ` [pve-devel] [PATCH manager v10 09/13] ui: add form/TagEdit.js Dominik Csapak
2022-11-16 15:00 ` Thomas Lamprecht
2022-11-16 15:02 ` Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH manager v10 10/13] ui: {lxc, qemu}/Config: show Tags and make them editable Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH manager v10 11/13] ui: tree/ResourceTree: show Tags in tree Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH manager v10 12/13] ui: add tags to ResourceGrid and GlobalSearchField Dominik Csapak
2022-11-15 13:02 ` [pve-devel] [PATCH manager v10 13/13] ui: implement tag ordering from datacenter.cfg Dominik Csapak
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20221116095038.vrvn43aeq33vrame@casey.proxmox.com \
--to=w.bumiller@proxmox.com \
--cc=d.csapak@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox