From: Alexander Graf <agraf@suse.de>
To: "Michael S. Tsirkin" <mst@redhat.com>
Cc: kvm list <kvm@vger.kernel.org>, Gleb Natapov <gleb@redhat.com>,
Muli Ben-Yehuda <muli@il.ibm.com>,
"Daniel P. Berrange" <berrange@redhat.com>
Subject: Re: [PATCH] Inform users about busy device assignment attempt
Date: Tue, 15 Dec 2009 16:18:00 +0100 [thread overview]
Message-ID: <4B27A8A8.7010906@suse.de> (raw)
In-Reply-To: <20091211113839.GE29972@redhat.com>
Michael S. Tsirkin wrote:
> On Fri, Dec 11, 2009 at 12:06:26AM +0100, Alexander Graf wrote:
>
>> When using -pcidevice on a device that is already in use by a kernel driver
>> all the user gets is the following (very useful) information:
>>
>> Failed to assign device "04:00.0" : Device or resource busy
>> Failed to deassign device "04:00.0" : Invalid argument
>> Error initializing device pci-assign
>>
>> Since I usually prefer to have my computer do the thinking for me, I figured
>> it might be a good idea to check and see if a device is actually used by a
>> driver. If so, tell the user.
>>
>> So with this patch applied you get the following output:
>>
>> Failed to assign device "04:00.0" : Device or resource busy
>> *** The driver 'igb' is occupying your device 04:00.0.
>> ***
>> *** You can try the following commands to free it:
>> ***
>> *** $ echo "8086 150a" > /sys/bus/pci/drivers/pci-stub/new_id
>> *** $ echo "0000:04:00.0" > /sys/bus/pci/drivers/igb/unbind
>> *** $ echo "0000:04:00.0" > /sys/bus/pci/drivers/pci-stub/bind
>> *** $ echo "8086 150a" > /sys/bus/pci/drivers/pci-stub/remove_id
>> ***
>> Failed to deassign device "04:00.0" : Invalid argument
>> Error initializing device pci-assign
>>
>> That should keep people like me from doing the most obvious misuses :-).
>>
>> CC: Daniel P. Berrange <berrange@redhat.com>
>> Signed-off-by: Alexander Graf <agraf@suse.de>
>>
>
> Minor nits and a bug.
>
>
>> ---
>>
>> v1 -> v2:
>>
>> - add more helpful guidance thanks to Daniel Berrange
>>
>> v2 -> v3:
>>
>> - clear name variable before using it, thus 0-terminating the string
>> - fix region numbers
>> - use correct unbind/bind names
>> ---
>> hw/device-assignment.c | 109 +++++++++++++++++++++++++++++++++++++-----------
>> 1 files changed, 85 insertions(+), 24 deletions(-)
>>
>> diff --git a/hw/device-assignment.c b/hw/device-assignment.c
>> index 5cee929..98faa83 100644
>> --- a/hw/device-assignment.c
>> +++ b/hw/device-assignment.c
>> @@ -564,14 +564,44 @@ static int assigned_dev_register_regions(PCIRegion *io_regions,
>> return 0;
>> }
>>
>> +static int get_real_id(const char *devpath, const char *idname, uint16_t *val)
>> +{
>> + FILE *f;
>> + char name[128];
>>
>
> let's not introduce arbitraty file name length limitations.
> strlen is not hard to use. I know all this module is
> broken this way, but let's not add more.
>
It's just a move of existing code. I tried to change it as little as
possible. Cleanups for that are welcome for later.
>
>> + long id;
>> +
>> + snprintf(name, sizeof(name), "%s%s", devpath, idname);
>> + f = fopen(name, "r");
>> + if (f == NULL) {
>> + fprintf(stderr, "%s: %s: %m\n", __func__, name);
>> + return -1;
>> + }
>> + if (fscanf(f, "%li\n", &id) == 1) {
>> + *val = id;
>> + }
>>
>
> handle fscanf error?
>
Interesting. I don't think it was done before, but I can put it in.
>
>> + fclose(f);
>> +
>> + return 0;
>> +}
>> +
>> +static int get_real_vendor_id(const char *devpath, uint16_t *val)
>> +{
>> + return get_real_id(devpath, "vendor", val);
>> +}
>> +
>> +static int get_real_device_id(const char *devpath, uint16_t *val)
>> +{
>> + return get_real_id(devpath, "device", val);
>> +}
>> +
>> static int get_real_device(AssignedDevice *pci_dev, uint8_t r_bus,
>> uint8_t r_dev, uint8_t r_func)
>> {
>> char dir[128], name[128];
>> - int fd, r = 0;
>> + int fd, r = 0, v;
>> FILE *f;
>> unsigned long long start, end, size, flags;
>> - unsigned long id;
>> + uint16_t id;
>> struct stat statbuf;
>> PCIRegion *rp;
>> PCIDevRegions *dev = &pci_dev->real_device;
>> @@ -637,31 +667,21 @@ again:
>>
>> fclose(f);
>>
>> - /* read and fill device ID */
>> - snprintf(name, sizeof(name), "%svendor", dir);
>> - f = fopen(name, "r");
>> - if (f == NULL) {
>> - fprintf(stderr, "%s: %s: %m\n", __func__, name);
>> + /* read and fill vendor ID */
>> + v = get_real_vendor_id(dir, &id);
>> + if (v) {
>> return 1;
>> }
>> - if (fscanf(f, "%li\n", &id) == 1) {
>> - pci_dev->dev.config[0] = id & 0xff;
>> - pci_dev->dev.config[1] = (id & 0xff00) >> 8;
>> - }
>> - fclose(f);
>> + pci_dev->dev.config[0] = id & 0xff;
>> + pci_dev->dev.config[1] = (id & 0xff00) >> 8;
>>
>>
>
> this seems an unrelated cleanup?
> If so better as a separate patch?
>
It's the code move. I split it now.
>
>
>> - /* read and fill vendor ID */
>> - snprintf(name, sizeof(name), "%sdevice", dir);
>> - f = fopen(name, "r");
>> - if (f == NULL) {
>> - fprintf(stderr, "%s: %s: %m\n", __func__, name);
>> + /* read and fill device ID */
>> + v = get_real_device_id(dir, &id);
>> + if (v) {
>> return 1;
>> }
>> - if (fscanf(f, "%li\n", &id) == 1) {
>> - pci_dev->dev.config[2] = id & 0xff;
>> - pci_dev->dev.config[3] = (id & 0xff00) >> 8;
>> - }
>> - fclose(f);
>> + pci_dev->dev.config[2] = id & 0xff;
>> + pci_dev->dev.config[3] = (id & 0xff00) >> 8;
>>
>> /* dealing with virtual function device */
>> snprintf(name, sizeof(name), "%sphysfn/", dir);
>> @@ -739,7 +759,9 @@ static uint32_t calc_assigned_dev_id(uint8_t bus, uint8_t devfn)
>> static int assign_device(AssignedDevice *dev)
>> {
>> struct kvm_assigned_pci_dev assigned_dev_data;
>> - int r;
>> + char name[128], dir[128], driver[128], *ns;
>>
>
> Yes 128 will be enough for now. But it's pretty ugly.
> In this case, something like
> char dir[] = "/sys/bus/pci/devices/0000:00:00.0/";
> will allocate just enough memory.
> Or use MAX PATH.
>
Used MAX_PATH now.
>
>> + uint16_t vendor_id, device_id;
>> + int r, v;
>>
>> memset(&assigned_dev_data, 0, sizeof(assigned_dev_data));
>> assigned_dev_data.assigned_dev_id =
>> @@ -761,9 +783,48 @@ static int assign_device(AssignedDevice *dev)
>> #endif
>>
>> r = kvm_assign_pci_device(kvm_context, &assigned_dev_data);
>> - if (r < 0)
>> + if (r < 0) {
>>
>
>
> Please put all of the below in a separate function.
>
Ok.
>
>> fprintf(stderr, "Failed to assign device \"%s\" : %s\n",
>> dev->dev.qdev.id, strerror(-r));
>> +
>> + snprintf(dir, sizeof(dir),
>>
>
> snprintf? So you worry about overflowing dir?
> But dir will not be 0 terminated on overflow,
> so use of %s below would crash anyway.
> As in fact we know this can not overflow, just use sprintf.
>
Ok.
>
>> + "/sys/bus/pci/devices/0000:%02x:%02x.%x/",
>> + dev->host.bus, dev->host.dev, dev->host.func);
>>
>
> This assumes domain 0. I know multidomain is
> broken with device assignment, but pls add
> TOIDO here so we don't forget to fix it.
>
Ok.
>
>> +
>> + snprintf(name, sizeof(name), "%sdriver", dir);
>>
>
> So why do sprintf twice? Just put "driver" as part
> of the template above.
>
We're using dir later in the code.
>
>> +
>> + memset(driver, 0, sizeof(driver));
>>
>
> just initialize driver to 0 by = {};
>
>
That initializes it to 0? I mean, all elements?
>> + v = readlink(name, driver, sizeof(driver));
>>
>
> So if readlink fills up all of driver, strrchr
> below will cause coredump, right? Better check v against
> sizeof driver.
>
Ok.
>
>> + if ((v <= 0) || !(ns = strrchr(driver, '/'))) {
>> + return r;
>>
>
> Add some fprintf here. Maybe report errno as well.
>
Ok.
>
>> + }
>> +
>> + ns++;
>> +
>> + if (get_real_vendor_id(dir, &vendor_id) ||
>> + get_real_device_id(dir, &device_id)) {
>> + return r;
>>
>
> And here.
>
Yep.
>
>> + }
>> +
>> + fprintf(stderr, "*** The driver '%s' is occupying your device "
>> + "%02x:%02x.%x.\n",
>> + ns, dev->host.bus, dev->host.dev, dev->host.func);
>> + fprintf(stderr, "***\n");
>> + fprintf(stderr, "*** You can try the following commands to free "
>> + "it:\n");
>> + fprintf(stderr, "***\n");
>> + fprintf(stderr, "*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/"
>> + "pci-stub/new_id\n", vendor_id, device_id);
>> + fprintf(stderr, "*** $ echo \"0000:%02x:%02x.%x\" > /sys/bus/pci"
>> + "/drivers/%s /unbind\n",
>> + dev->host.bus, dev->host.dev, dev->host.func, ns);
>> + fprintf(stderr, "*** $ echo \"0000:%02x:%02x.%x\" > /sys/bus/pci"
>> + "/drivers/ pci-stub/bind\n",
>> + dev->host.bus, dev->host.dev, dev->host.func);
>> + fprintf(stderr, "*** $ echo \"%x %x\" > /sys/bus/pci/drivers/pci-stub"
>> + "/remove_id\n", vendor_id, device_id);
>> + fprintf(stderr, "***\n");
>>
>
> above assumes domain zero. Please add a TODO to fix.
>
Same as above, right? In fact, a lot of the code assumes that so it's
more of a generic TODO :-(.
Alex
next prev parent reply other threads:[~2009-12-15 15:18 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-12-10 23:06 [PATCH] Enable non page boundary BAR device assignment Alexander Graf
2009-12-10 23:06 ` [PATCH] Inform users about busy device assignment attempt Alexander Graf
2009-12-11 11:38 ` Michael S. Tsirkin
2009-12-15 15:18 ` Alexander Graf [this message]
2009-12-15 15:18 ` Michael S. Tsirkin
2009-12-11 11:05 ` [PATCH] Enable non page boundary BAR device assignment Michael S. Tsirkin
2009-12-15 18:16 ` Alexander Graf
2009-12-15 18:20 ` Michael S. Tsirkin
2009-12-15 18:24 ` Alexander Graf
2009-12-16 20:12 ` Muli Ben-Yehuda
-- strict thread matches above, loose matches on Subject: below --
2009-12-09 20:13 [PATCH] Inform users about busy device assignment attempt Alexander Graf
2009-12-09 18:04 Alexander Graf
2009-12-09 18:19 ` Daniel P. Berrange
2009-12-09 19:18 ` Alexander Graf
2009-12-09 19:40 ` Daniel P. Berrange
2009-12-09 19:44 ` Alexander Graf
2009-12-09 20:14 ` Daniel P. Berrange
2009-12-09 20:15 ` Alexander Graf
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=4B27A8A8.7010906@suse.de \
--to=agraf@suse.de \
--cc=berrange@redhat.com \
--cc=gleb@redhat.com \
--cc=kvm@vger.kernel.org \
--cc=mst@redhat.com \
--cc=muli@il.ibm.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 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.