All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Alex Bennée" <alex.bennee@linaro.org>
To: Viresh Kumar <viresh.kumar@linaro.org>
Cc: Vincent Guittot <vincent.guittot@linaro.org>,
	Jie Deng <jie.deng@intel.com>, Bill Mills <bill.mills@linaro.org>,
	qemu-devel@nongnu.org, Arnd Bergmann <arnd.bergmann@linaro.com>,
	Mike Holmes <mike.holmes@linaro.org>,
	stratos-dev@op-lists.linaro.org
Subject: Re: [PATCH V2 4/6] tools/vhost-user-i2c: Add backend driver
Date: Thu, 01 Apr 2021 14:43:51 +0100	[thread overview]
Message-ID: <87eefu13c4.fsf@linaro.org> (raw)
In-Reply-To: <e0adcd9552cee4de0ee844f6b3c87fb2b2f2357c.1617278395.git.viresh.kumar@linaro.org>


Viresh Kumar <viresh.kumar@linaro.org> writes:

> This adds the vhost-user backend driver to support virtio based I2C and
> SMBUS devices.
>
> vhost-user-i2c --help
>
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
<snip>
> +
> +static VI2cAdapter *vi2c_create_adapter(int32_t bus, uint16_t client_addr[],
> +                                        int32_t n_client)
> +{
> +    VI2cAdapter *adapter;
> +    char path[20];

If you use:

    g_autofree char *path = NULL;

If fact you could also use g_autofree with adapter to make you exit case
a little easier.

> +    uint64_t funcs;
> +    int32_t fd, i;
> +
> +    if (bus < 0) {
> +        return NULL;
> +    }
> +
> +    adapter = g_malloc0(sizeof(*adapter));

g_malloc will abort() on lack of memory so you can skip the check here.

> +    if (!adapter) {
> +        g_printerr("failed to alloc adapter");
> +        return NULL;
> +    }
> +
> +    snprintf(path, sizeof(path), "/dev/i2c-%d", bus);
> +    path[sizeof(path) - 1] = '\0';

then this becomes:

  path = g_strdup_printf("/dev/i2c-%d", bus);

> +
> +    fd = open(path, O_RDWR);
> +    if (fd < 0) {
> +        g_printerr("virtio_i2c: failed to open %s\n", path);
> +        goto fail;
> +    }
> +
> +    adapter->fd = fd;
> +    adapter->bus = bus;
> +
> +    if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
> +        g_printerr("virtio_i2c: failed to get functionality %s: %d\n", path,
> +                   errno);
> +        goto close_fd;
> +    }
> +
> +    if (funcs & I2C_FUNC_I2C) {
> +        adapter->smbus = false;
> +    } else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
> +        adapter->smbus = true;
> +    } else {
> +        g_printerr("virtio_i2c: invalid functionality %lx\n", funcs);
> +        goto close_fd;
> +    }
> +
> +    for (i = 0; i < n_client; i++) {
> +        if (client_addr[i]) {
> +            if (!vi2c_set_client_addr(adapter, client_addr[i])) {
> +                goto close_fd;
> +            }
> +
> +            if (adapter->clients[client_addr[i]]) {
> +                g_printerr("client addr 0x%x repeat, not allowed.\n",
> +                           client_addr[i]);
> +                goto close_fd;
> +            }
> +
> +            adapter->clients[client_addr[i]] = true;
> +            if (verbose) {
> +                g_print("Added client 0x%x to bus %u\n", client_addr[i], bus);
> +            }
> +        }
> +    }
> +
> +    if (verbose) {
> +        g_print("Added adapter: bus: %d, func %s\n", bus,
> +                adapter->smbus ? "smbus" : "i2c");
> +    }
> +    return adapter;

This would then be:

  return g_steal_pointer(adapter);

> +
> +close_fd:
> +    close(fd);
> +fail:
> +    g_free(adapter);
> +    return NULL;
> +}
> +
> +/*
> + * Virtio I2C device list format.
> + *
> + * <bus>:<client_addr>[:<client_addr>],
> + * [<bus>:<client_addr>[:<client_addr>]]
> + *
> + * bus (dec): adatper bus number.
> + *     e.g. 2 for /dev/i2c-2
> + * client_addr (hex): address for client device
> + *     e.g. 0x1C or 1C
> + *
> + * Example: --device-list="2:0x1c:0x20,3:0x10:0x2c"
> + *
> + * Note: client address can not repeat.
> + */
> +static int32_t vi2c_parse(VuI2c *i2c)
> +{
> +    uint16_t client_addr[MAX_I2C_VDEV];
> +    int32_t n_adapter = 0, n_client;
> +    int64_t addr, bus;
> +    const char *cp, *t;
> +
> +    while (device_list) {
> +        /* Read <bus>:<client_addr>[:<client_addr>] entries one by one */
> +        cp = strsep(&device_list, ",");

The glib approach would be to use g_strsplit(device_list, ",", 0) which
you can then iterate over with something like:

    for (i = 0; cp[i] != NULL; i++) {

    } 

    g_strfreev(cp);

> +
> +        if (!cp || *cp == '\0') {
> +            break;
> +        }
> +
> +        if (n_adapter == MAX_I2C_ADAPTER) {
> +            g_printerr("too many adapter (%d), only support %d\n", n_adapter,
> +                       MAX_I2C_ADAPTER);
> +            goto out;
> +        }
> +
> +        if (qemu_strtol(cp, &t, 10, &bus) || bus < 0) {
> +            g_printerr("Invalid bus number %s\n", cp);
> +            goto out;
> +        }
> +
> +        cp = t;
> +        n_client = 0;
> +
> +        /* Parse clients <client_addr>[:<client_addr>] entries one by
> one */

Then this would be:

     **dev = g_strsplit(cp[i], ":", 2);
     if (dev && dev[0]) {

         if (dev[1]) {
         }
     }
     g_freestrv(dev);

which would avoid you manually iterating over the string.

> +        while (cp != NULL && *cp != '\0') {
> +            if (*cp == ':') {
> +                cp++;
> +            }
> +
> +            if (n_client == MAX_I2C_VDEV) {
> +                g_printerr("too many devices (%d), only support %d\n",
> +                           n_client, MAX_I2C_VDEV);
> +                goto out;
> +            }
> +
> +            if (qemu_strtol(cp, &t, 16, &addr) ||
> +                addr < 0 || addr > MAX_I2C_VDEV) {
> +                g_printerr("Invalid address %s : %lx\n", cp, addr);
> +                goto out;
> +            }
> +
> +            client_addr[n_client++] = addr;
> +            cp = t;
> +            if (verbose) {
> +                g_print("i2c adapter %ld:0x%lx\n", bus, addr);
> +            }
> +        }
> +
> +        i2c->adapter[n_adapter] = vi2c_create_adapter(bus, client_addr,
> +                                                      n_client);
> +        if (!i2c->adapter[n_adapter]) {
> +            goto out;
> +        }
> +
> +        n_adapter++;
> +    }
> +
> +    if (!n_adapter) {
> +        g_printerr("Failed to add any adapters\n");
> +        return -1;
> +    }
> +
> +    i2c->adapter_num = n_adapter;
> +
> +    if (!vi2c_map_adapters(i2c)) {
> +        return 0;
> +    }
> +
> +out:
> +    vi2c_remove_adapters(i2c);
> +    return -1;
> +}
> +
<snip>
> +
> +static void vi2c_handle_ctrl(VuDev *dev, int qidx)
> +{
> +    VuVirtq *vq = vu_get_queue(dev, qidx);
> +    struct virtio_i2c_out_hdr *out_hdr;
> +    size_t i, count = 0, len, in_hdr_len;
> +    struct i2c_msg *msgs;
> +    VuVirtqElement *elem;
> +    uint8_t status;
> +    struct {
> +        struct virtio_i2c_in_hdr *in_hdr;
> +        VuVirtqElement *elem;
> +        size_t size;
> +    } *info;
> +
> +    /* Count number of messages */
> +    do {
> +        elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement));
> +    } while (elem && ++count);

Can you expand on this comment as to why we can't process each message
as we come to it?

> +
> +    if (!count) {
> +        if (verbose) {
> +            g_printerr("Virtqueue can't have 0 elements\n");
> +        }
> +        return;
> +    }
> +
> +    /* Rewind everything, now that we have counted the number of messages */
> +    vu_queue_rewind(dev, vq, count);
> +
> +    if (verbose) {
> +        g_print("Received %ld messages in virtqueue\n", count);
> +    }
> +
> +    /* Allocate memory for msgs and info */
> +    msgs = g_malloc0_n(count, sizeof(*msgs) + sizeof(*info));
> +    if (!msgs) {
> +        g_printerr("failed to allocate space for messages\n");
> +        return;
> +    }

g_malloc0_n will abort() or succeed. Could this be a g_new and be typed
in some way?

> +    info = (void *)(msgs + count);
> +
> +    for (i = 0; i < count; i++) {
> +        elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement));
> +        if (!elem) {
> +            g_printerr("Failed to pop element: %ld : %ld\n", i, count);
> +            goto out;
> +        }

not withstanding the above comment this seems like an assert() because
it should never fail?

> +        info[i].elem = elem;
> +        info[i].size = sizeof(*info[i].in_hdr);
> +
> +        g_debug("%s: got queue (in %d, out %d)", __func__, elem->in_num,
> +                elem->out_num);
> +
> +        /* Validate size of "out" header */
> +        if (elem->out_sg[0].iov_len != sizeof(*out_hdr)) {
> +            g_warning("%s: Invalid out hdr %zu : %zu\n", __func__,
> +                      elem->out_sg[0].iov_len, sizeof(*out_hdr));
> +            goto out;
> +        }
> +
> +        out_hdr = elem->out_sg[0].iov_base;
> +
> +        /* Bit 0 is reserved in virtio spec */
> +        msgs[i].addr = le16toh(out_hdr->addr) >> 1;
> +
> +        /* Read Operation */
> +        if (elem->out_num == 1 && elem->in_num == 2) {
> +            len = elem->in_sg[0].iov_len;
> +            if (!len) {
> +                g_warning("%s: Read buffer length can't be zero\n", __func__);
> +                goto out;
> +            }
> +
> +            msgs[i].buf = elem->in_sg[0].iov_base;
> +            msgs[i].flags = I2C_M_RD;
> +            msgs[i].len = len;
> +
> +            info[i].in_hdr = elem->in_sg[1].iov_base;
> +            info[i].size += len;
> +            in_hdr_len = elem->in_sg[1].iov_len;
> +        } else if (elem->out_num == 2 && elem->in_num == 1) {
> +            /* Write Operation */
> +            len = elem->out_sg[1].iov_len;
> +            if (!len) {
> +                g_warning("%s: Write buffer length can't be zero\n", __func__);
> +                goto out;
> +            }
> +
> +            msgs[i].buf = elem->out_sg[1].iov_base;
> +            msgs[i].flags = 0;
> +            msgs[i].len = len;
> +
> +            info[i].in_hdr = elem->in_sg[0].iov_base;
> +            in_hdr_len = elem->in_sg[0].iov_len;
> +        } else {
> +            g_warning("%s: Transfer type not supported (in %d, out %d)\n",
> +                      __func__, elem->in_num, elem->out_num);
> +            goto out;
> +        }
> +
> +        /* Validate size of "in" header */
> +        if (in_hdr_len != sizeof(*(info[i].in_hdr))) {
> +            g_warning("%s: Invalid in hdr %zu : %zu\n", __func__, in_hdr_len,
> +                      sizeof(*(info[i].in_hdr)));
> +            goto out;
> +        }
> +    }
> +
> +    status = vi2c_xfer(dev, msgs, count);
> +
> +    for (i = 0; i < count; i++) {
> +        info[i].in_hdr->status = status;
> +        vu_queue_push(dev, vq, info[i].elem, info[i].size);
> +    }
> +
> +    vu_queue_notify(dev, vq);
> +
> +out:
> +    g_free(msgs);
> +}
> +
<snip>
> diff --git a/tools/vhost-user-i2c/meson.build b/tools/vhost-user-i2c/meson.build
> new file mode 100644
> index 000000000000..f71e9fec7df6
> --- /dev/null
> +++ b/tools/vhost-user-i2c/meson.build
> @@ -0,0 +1,10 @@
> +executable('vhost-user-i2c', files(
> +  'main.c'),
> +  dependencies: [qemuutil, glib, gio],
> +  install: true,
> +  install_dir: get_option('libexecdir'))
> +
> +configure_file(input: '50-qemu-i2c.json.in',
> +               output: '50-qemu-i2c.json',
> +               configuration: config_host,

FYI this now needs to be:

               configuration: { 'libexecdir' : get_option('prefix') / get_option('libexecdir') },

to properly pass libexecdir above.

> +               install_dir: qemu_datadir / 'vhost-user')


-- 
Alex Bennée


  reply	other threads:[~2021-04-01 14:35 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-01 12:12 [PATCH V2 0/6] virtio: Implement generic vhost-user-i2c backend Viresh Kumar
2021-04-01 12:12 ` [PATCH V2 1/6] !Merge: Update virtio headers from kernel Viresh Kumar
2021-04-01 12:12 ` [PATCH V2 2/6] hw/virtio: add boilerplate for vhost-user-i2c device Viresh Kumar
2021-04-01 13:43   ` Alex Bennée
2021-04-01 12:12 ` [PATCH V2 3/6] hw/virtio: add vhost-user-i2c-pci boilerplate Viresh Kumar
2021-04-01 12:12 ` [PATCH V2 4/6] tools/vhost-user-i2c: Add backend driver Viresh Kumar
2021-04-01 13:43   ` Alex Bennée [this message]
2021-04-05  9:39     ` Viresh Kumar
2021-04-02  2:55   ` Jie Deng
2021-04-05  5:28     ` Viresh Kumar
2021-04-05  9:38   ` [PATCH V2.1 " Viresh Kumar
2021-04-01 12:12 ` [PATCH V2 5/6] docs: add a man page for vhost-user-i2c Viresh Kumar
2021-04-01 12:12 ` [PATCH V2 6/6] MAINTAINERS: Add entry for virtio-i2c Viresh Kumar

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=87eefu13c4.fsf@linaro.org \
    --to=alex.bennee@linaro.org \
    --cc=arnd.bergmann@linaro.com \
    --cc=bill.mills@linaro.org \
    --cc=jie.deng@intel.com \
    --cc=mike.holmes@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=stratos-dev@op-lists.linaro.org \
    --cc=vincent.guittot@linaro.org \
    --cc=viresh.kumar@linaro.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.