All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Inform users about busy device assignment attempt
@ 2009-12-09 20:13 Alexander Graf
  0 siblings, 0 replies; 12+ messages in thread
From: Alexander Graf @ 2009-12-09 20:13 UTC (permalink / raw)
  To: kvm; +Cc: Daniel P. Berrange

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 "04:00.0" > /sys/bus/pci/drivers/igb/unbind
  *** $ echo "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>

---

v1 -> v2:

  - add more helpful guidance thanks to Daniel Berrange
---
 hw/device-assignment.c |  106 +++++++++++++++++++++++++++++++++++++----------
 1 files changed, 83 insertions(+), 23 deletions(-)

diff --git a/hw/device-assignment.c b/hw/device-assignment.c
index beb488c..9fbddcf 100644
--- a/hw/device-assignment.c
+++ b/hw/device-assignment.c
@@ -572,6 +572,36 @@ 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];
+    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;
+    }
+    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)
 {
@@ -579,7 +609,7 @@ static int get_real_device(AssignedDevice *pci_dev, uint8_t r_bus,
     int fd, r = 0;
     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;
@@ -645,31 +675,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 */
+    r = get_real_vendor_id(dir, &id);
+    if (r) {
         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;
 
-    /* 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 */
+    r = get_real_device_id(dir, &id);
+    if (r) {
         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);
@@ -747,7 +767,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;
+    uint16_t vendor_id, device_id;
+    int r, v;
 
     memset(&assigned_dev_data, 0, sizeof(assigned_dev_data));
     assigned_dev_data.assigned_dev_id  =
@@ -769,9 +791,47 @@ static int assign_device(AssignedDevice *dev)
 #endif
 
     r = kvm_assign_pci_device(kvm_context, &assigned_dev_data);
-    if (r < 0)
+    if (r < 0) {
 	fprintf(stderr, "Failed to assign device \"%s\" : %s\n",
                 dev->dev.qdev.id, strerror(-r));
+
+        snprintf(dir, sizeof(dir),
+                 "/sys/bus/pci/devices/0000:%02x:%02x.%x/",
+	         dev->host.bus, dev->host.dev, dev->host.func);
+
+        snprintf(name, sizeof(name), "%sdriver", dir);
+
+        v = readlink(name, driver, sizeof(driver));
+        if ((v <= 0) || !(ns = strrchr(driver, '/'))) {
+            return r;
+        }
+
+        ns++;
+
+        if (get_real_vendor_id(dir, &vendor_id) ||
+            get_real_device_id(dir, &device_id)) {
+            return r;
+        }
+
+        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 \"%02x:%02x.%x\" > /sys/bus/pci/drivers/%s"
+                        "/unbind\n", dev->host.bus, dev->host.dev,
+                                     dev->host.func, ns);
+        fprintf(stderr, "*** $ echo \"%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");
+    }
     return r;
 }
 
-- 
1.6.0.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH] Inform users about busy device assignment attempt
  2009-12-15 15:18     ` Alexander Graf
@ 2009-12-15 15:18       ` Michael S. Tsirkin
  0 siblings, 0 replies; 12+ messages in thread
From: Michael S. Tsirkin @ 2009-12-15 15:18 UTC (permalink / raw)
  To: Alexander Graf
  Cc: kvm list, Gleb Natapov, Muli Ben-Yehuda, Daniel P. Berrange

On Tue, Dec 15, 2009 at 04:18:00PM +0100, Alexander Graf wrote:
> 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?

Yes.

> >> +        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


Yes it is, unfortunately :(


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Inform users about busy device assignment attempt
  2009-12-11 11:38   ` Michael S. Tsirkin
@ 2009-12-15 15:18     ` Alexander Graf
  2009-12-15 15:18       ` Michael S. Tsirkin
  0 siblings, 1 reply; 12+ messages in thread
From: Alexander Graf @ 2009-12-15 15:18 UTC (permalink / raw)
  To: Michael S. Tsirkin
  Cc: kvm list, Gleb Natapov, Muli Ben-Yehuda, Daniel P. Berrange

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

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Inform users about busy device assignment attempt
  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
  0 siblings, 1 reply; 12+ messages in thread
From: Michael S. Tsirkin @ 2009-12-11 11:38 UTC (permalink / raw)
  To: Alexander Graf
  Cc: kvm list, Gleb Natapov, Muli Ben-Yehuda, Daniel P. Berrange

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.

> +    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?

> +    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?


> -    /* 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.

> +    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.

>  	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.

> +                 "/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.

> +
> +        snprintf(name, sizeof(name), "%sdriver", dir);

So why do sprintf twice? Just put "driver" as part
of the template above.

> +
> +        memset(driver, 0, sizeof(driver));

just initialize driver to 0 by = {};

> +        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.

> +        if ((v <= 0) || !(ns = strrchr(driver, '/'))) {
> +            return r;

Add some fprintf here. Maybe report errno as well.

> +        }
> +
> +        ns++;
> +
> +        if (get_real_vendor_id(dir, &vendor_id) ||
> +            get_real_device_id(dir, &device_id)) {
> +            return r;

And here.

> +        }
> +
> +        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.

> +    }
>      return r;
>  }
>  
> -- 
> 1.6.0.2

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH] Inform users about busy device assignment attempt
  2009-12-10 23:06 [PATCH] Enable non page boundary BAR device assignment Alexander Graf
@ 2009-12-10 23:06 ` Alexander Graf
  2009-12-11 11:38   ` Michael S. Tsirkin
  0 siblings, 1 reply; 12+ messages in thread
From: Alexander Graf @ 2009-12-10 23:06 UTC (permalink / raw)
  To: kvm list
  Cc: Gleb Natapov, Michael S. Tsirkin, Muli Ben-Yehuda, Daniel P. Berrange

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>

---

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];
+    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;
+    }
+    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;
 
-    /* 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;
+    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) {
 	fprintf(stderr, "Failed to assign device \"%s\" : %s\n",
                 dev->dev.qdev.id, strerror(-r));
+
+        snprintf(dir, sizeof(dir),
+                 "/sys/bus/pci/devices/0000:%02x:%02x.%x/",
+	         dev->host.bus, dev->host.dev, dev->host.func);
+
+        snprintf(name, sizeof(name), "%sdriver", dir);
+
+        memset(driver, 0, sizeof(driver));
+        v = readlink(name, driver, sizeof(driver));
+        if ((v <= 0) || !(ns = strrchr(driver, '/'))) {
+            return r;
+        }
+
+        ns++;
+
+        if (get_real_vendor_id(dir, &vendor_id) ||
+            get_real_device_id(dir, &device_id)) {
+            return r;
+        }
+
+        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");
+    }
     return r;
 }
 
-- 
1.6.0.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH] Inform users about busy device assignment attempt
  2009-12-09 20:14         ` Daniel P. Berrange
@ 2009-12-09 20:15           ` Alexander Graf
  0 siblings, 0 replies; 12+ messages in thread
From: Alexander Graf @ 2009-12-09 20:15 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: kvm

Daniel P. Berrange wrote:
> On Wed, Dec 09, 2009 at 08:44:06PM +0100, Alexander Graf wrote:
>   
>> Daniel P. Berrange wrote:
>>     
>>> On Wed, Dec 09, 2009 at 08:18:21PM +0100, Alexander Graf wrote:
>>>   
>>>       
>>>> Daniel P. Berrange wrote:
>>>>     
>>>>         
>>>>> Unconditionally telling people to run rmmod is a pretty dangerous thing
>>>>> todo. If they typod and gave the PCI addr of their disk controller instead
>>>>> of the NIC, they'll be less than happy at the results of our recommended
>>>>> command to "fix" the error. Likewise if they have multiple devices using
>>>>> the same driver & just want to assign one of them. I think it is safer to
>>>>> just have the first bit of your proposed error message
>>>>>
>>>>>   "The device 04:00.0 is in use by the kernel driver 'igb'."
>>>>>
>>>>>
>>>>> NB 'rmmod' is not the ideal approach for PCI assignment. It is better
>>>>> to explicitly re-bind the device to 'pcistub' because that ensures that
>>>>> no other driver will ever be able to reclaim the device.
>>>>>   
>>>>>       
>>>>>           
>>>> Oh - mind to get into detail there? It'd be great if we could tell users
>>>> an even better way to unbind their device from the driver than rmmod :)
>>>>     
>>>>         
>>> The direct low level sysfs way involves the following steps
>>>
>>>   // Tell pci-stub to accept a particular vendor+product ID binding
>>>   # echo "8086 27cb" > /sys/bus/pci/drivers/pci-stub/new_id
>>>
>>>   // Remove device from existing PCI driver
>>>   # echo "00:1d.3" > /sys/bus/pci/drivers/e1000/unbind
>>>
>>>   // Add device to pci-stub PCI driver
>>>   # echo "00:1d.3" > /sys/bus/pci/drivers/pci-stub/bind
>>>
>>>   // Tell pci-stub to stop accepting a vendor+product ID binding
>>>   # echo "8086 27cb" > /sys/bus/pci/drivers/pci-stub/remove_id
>>>
>>> The reason for  that last step is that if you have multiple devices of
>>> the same vendor+product, you don't want a later hotplug event to bind
>>> the new device to pci-stub too !
>>>   
>>>       
>> So what would you think if we'd just print out those 4 commands to the
>> user, so people who don't use libvirt still get guidance when using
>> -pcidevice :). They should be fairly easy to construct from the
>> information we have.
>>     
>
> I think it is better to put that information in the QEMU docs for the
> -pcidevice argument where it can be properly explained in detail, 
> outlining the potential consequences of the actions. The error message
> could direct people to the docs.
>   

I personally like programs that guide me, so I guess it should be in both.

Alex

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Inform users about busy device assignment attempt
  2009-12-09 19:44       ` Alexander Graf
@ 2009-12-09 20:14         ` Daniel P. Berrange
  2009-12-09 20:15           ` Alexander Graf
  0 siblings, 1 reply; 12+ messages in thread
From: Daniel P. Berrange @ 2009-12-09 20:14 UTC (permalink / raw)
  To: Alexander Graf; +Cc: kvm

On Wed, Dec 09, 2009 at 08:44:06PM +0100, Alexander Graf wrote:
> Daniel P. Berrange wrote:
> > On Wed, Dec 09, 2009 at 08:18:21PM +0100, Alexander Graf wrote:
> >   
> >> Daniel P. Berrange wrote:
> >>     
> >>> Unconditionally telling people to run rmmod is a pretty dangerous thing
> >>> todo. If they typod and gave the PCI addr of their disk controller instead
> >>> of the NIC, they'll be less than happy at the results of our recommended
> >>> command to "fix" the error. Likewise if they have multiple devices using
> >>> the same driver & just want to assign one of them. I think it is safer to
> >>> just have the first bit of your proposed error message
> >>>
> >>>   "The device 04:00.0 is in use by the kernel driver 'igb'."
> >>>
> >>>
> >>> NB 'rmmod' is not the ideal approach for PCI assignment. It is better
> >>> to explicitly re-bind the device to 'pcistub' because that ensures that
> >>> no other driver will ever be able to reclaim the device.
> >>>   
> >>>       
> >> Oh - mind to get into detail there? It'd be great if we could tell users
> >> an even better way to unbind their device from the driver than rmmod :)
> >>     
> >
> > The direct low level sysfs way involves the following steps
> >
> >   // Tell pci-stub to accept a particular vendor+product ID binding
> >   # echo "8086 27cb" > /sys/bus/pci/drivers/pci-stub/new_id
> >
> >   // Remove device from existing PCI driver
> >   # echo "00:1d.3" > /sys/bus/pci/drivers/e1000/unbind
> >
> >   // Add device to pci-stub PCI driver
> >   # echo "00:1d.3" > /sys/bus/pci/drivers/pci-stub/bind
> >
> >   // Tell pci-stub to stop accepting a vendor+product ID binding
> >   # echo "8086 27cb" > /sys/bus/pci/drivers/pci-stub/remove_id
> >
> > The reason for  that last step is that if you have multiple devices of
> > the same vendor+product, you don't want a later hotplug event to bind
> > the new device to pci-stub too !
> >   
> 
> So what would you think if we'd just print out those 4 commands to the
> user, so people who don't use libvirt still get guidance when using
> -pcidevice :). They should be fairly easy to construct from the
> information we have.

I think it is better to put that information in the QEMU docs for the
-pcidevice argument where it can be properly explained in detail, 
outlining the potential consequences of the actions. The error message
could direct people to the docs.

Regards,
Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Inform users about busy device assignment attempt
  2009-12-09 19:40     ` Daniel P. Berrange
@ 2009-12-09 19:44       ` Alexander Graf
  2009-12-09 20:14         ` Daniel P. Berrange
  0 siblings, 1 reply; 12+ messages in thread
From: Alexander Graf @ 2009-12-09 19:44 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: kvm

Daniel P. Berrange wrote:
> On Wed, Dec 09, 2009 at 08:18:21PM +0100, Alexander Graf wrote:
>   
>> Daniel P. Berrange wrote:
>>     
>>> Unconditionally telling people to run rmmod is a pretty dangerous thing
>>> todo. If they typod and gave the PCI addr of their disk controller instead
>>> of the NIC, they'll be less than happy at the results of our recommended
>>> command to "fix" the error. Likewise if they have multiple devices using
>>> the same driver & just want to assign one of them. I think it is safer to
>>> just have the first bit of your proposed error message
>>>
>>>   "The device 04:00.0 is in use by the kernel driver 'igb'."
>>>
>>>
>>> NB 'rmmod' is not the ideal approach for PCI assignment. It is better
>>> to explicitly re-bind the device to 'pcistub' because that ensures that
>>> no other driver will ever be able to reclaim the device.
>>>   
>>>       
>> Oh - mind to get into detail there? It'd be great if we could tell users
>> an even better way to unbind their device from the driver than rmmod :)
>>     
>
> The direct low level sysfs way involves the following steps
>
>   // Tell pci-stub to accept a particular vendor+product ID binding
>   # echo "8086 27cb" > /sys/bus/pci/drivers/pci-stub/new_id
>
>   // Remove device from existing PCI driver
>   # echo "00:1d.3" > /sys/bus/pci/drivers/e1000/unbind
>
>   // Add device to pci-stub PCI driver
>   # echo "00:1d.3" > /sys/bus/pci/drivers/pci-stub/bind
>
>   // Tell pci-stub to stop accepting a vendor+product ID binding
>   # echo "8086 27cb" > /sys/bus/pci/drivers/pci-stub/remove_id
>
> The reason for  that last step is that if you have multiple devices of
> the same vendor+product, you don't want a later hotplug event to bind
> the new device to pci-stub too !
>   

So what would you think if we'd just print out those 4 commands to the
user, so people who don't use libvirt still get guidance when using
-pcidevice :). They should be fairly easy to construct from the
information we have.

Alex

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Inform users about busy device assignment attempt
  2009-12-09 19:18   ` Alexander Graf
@ 2009-12-09 19:40     ` Daniel P. Berrange
  2009-12-09 19:44       ` Alexander Graf
  0 siblings, 1 reply; 12+ messages in thread
From: Daniel P. Berrange @ 2009-12-09 19:40 UTC (permalink / raw)
  To: Alexander Graf; +Cc: kvm

On Wed, Dec 09, 2009 at 08:18:21PM +0100, Alexander Graf wrote:
> Daniel P. Berrange wrote:
> >
> > Unconditionally telling people to run rmmod is a pretty dangerous thing
> > todo. If they typod and gave the PCI addr of their disk controller instead
> > of the NIC, they'll be less than happy at the results of our recommended
> > command to "fix" the error. Likewise if they have multiple devices using
> > the same driver & just want to assign one of them. I think it is safer to
> > just have the first bit of your proposed error message
> >
> >   "The device 04:00.0 is in use by the kernel driver 'igb'."
> >
> >
> > NB 'rmmod' is not the ideal approach for PCI assignment. It is better
> > to explicitly re-bind the device to 'pcistub' because that ensures that
> > no other driver will ever be able to reclaim the device.
> >   
> 
> Oh - mind to get into detail there? It'd be great if we could tell users
> an even better way to unbind their device from the driver than rmmod :)

The direct low level sysfs way involves the following steps

  // Tell pci-stub to accept a particular vendor+product ID binding
  # echo "8086 27cb" > /sys/bus/pci/drivers/pci-stub/new_id

  // Remove device from existing PCI driver
  # echo "00:1d.3" > /sys/bus/pci/drivers/e1000/unbind

  // Add device to pci-stub PCI driver
  # echo "00:1d.3" > /sys/bus/pci/drivers/pci-stub/bind

  // Tell pci-stub to stop accepting a vendor+product ID binding
  # echo "8086 27cb" > /sys/bus/pci/drivers/pci-stub/remove_id

The reason for  that last step is that if you have multiple devices of
the same vendor+product, you don't want a later hotplug event to bind
the new device to pci-stub too !


At a higher level this is also possible with libvirt's nodedev-XXX commands

   // Find the name of device you want to detach
   # virsh nodedev-list --tree
    computer
     |
     +- net_computer_loopback
     +- pci_8086_2448
     |   |
     |   +- pci_104c_ac56
     |     
     +- pci_8086_27a0
     +- pci_8086_27a1
     |   |
     ...snip rest of info...

   // Check it really is the device you want to detach
   # virsh nodedev-dumpxml pci_8086_27a1
   <device>
     <name>pci_8086_27a1</name>
     <parent>computer</parent>
     <driver>
       <name>pcieport-driver</name>
     </driver>
     <capability type='pci'>
       <domain>0</domain>
       <bus>0</bus>
       <slot>1</slot>
       <function>0</function>
       <product id='0x27a1'>Mobile 945GM/PM/GMS, 943/940GML and 945GT Express PCI Express Root Port</product>
       <vendor id='0x8086'>Intel Corporation</vendor>
     </capability>
   </device>

   // Detach it from your host OS 
   # virsh nodedev-dettach pci_8086_27a1
   Device pci_8086_27a1 dettached

That nodedev-dettach command, does all 4 of the sysfs commands in one
go. Those nodedev-XXX commands are all usable, even if you're not using
libvirt for actually running guests. If you are using libvirt for running
the guests, then you don't even need those steps. Simply set the attribute
'managed=yes' for the <hostdev> in the guest config, and libvirt does the
device detach automatically & reattachs when shutting down the guest

Regards,
Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Inform users about busy device assignment attempt
  2009-12-09 18:19 ` Daniel P. Berrange
@ 2009-12-09 19:18   ` Alexander Graf
  2009-12-09 19:40     ` Daniel P. Berrange
  0 siblings, 1 reply; 12+ messages in thread
From: Alexander Graf @ 2009-12-09 19:18 UTC (permalink / raw)
  To: Daniel P. Berrange; +Cc: kvm

Daniel P. Berrange wrote:
> On Wed, Dec 09, 2009 at 07:04:13PM +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.
>>   *** Try running "rmmod igb" on the commandline
>>   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 :-).
>>     
>
> Unconditionally telling people to run rmmod is a pretty dangerous thing
> todo. If they typod and gave the PCI addr of their disk controller instead
> of the NIC, they'll be less than happy at the results of our recommended
> command to "fix" the error. Likewise if they have multiple devices using
> the same driver & just want to assign one of them. I think it is safer to
> just have the first bit of your proposed error message
>
>   "The device 04:00.0 is in use by the kernel driver 'igb'."
>
>
> NB 'rmmod' is not the ideal approach for PCI assignment. It is better
> to explicitly re-bind the device to 'pcistub' because that ensures that
> no other driver will ever be able to reclaim the device.
>   

Oh - mind to get into detail there? It'd be great if we could tell users
an even better way to unbind their device from the driver than rmmod :)

Alex


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Inform users about busy device assignment attempt
  2009-12-09 18:04 Alexander Graf
@ 2009-12-09 18:19 ` Daniel P. Berrange
  2009-12-09 19:18   ` Alexander Graf
  0 siblings, 1 reply; 12+ messages in thread
From: Daniel P. Berrange @ 2009-12-09 18:19 UTC (permalink / raw)
  To: Alexander Graf; +Cc: kvm

On Wed, Dec 09, 2009 at 07:04:13PM +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.
>   *** Try running "rmmod igb" on the commandline
>   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 :-).

Unconditionally telling people to run rmmod is a pretty dangerous thing
todo. If they typod and gave the PCI addr of their disk controller instead
of the NIC, they'll be less than happy at the results of our recommended
command to "fix" the error. Likewise if they have multiple devices using
the same driver & just want to assign one of them. I think it is safer to
just have the first bit of your proposed error message

  "The device 04:00.0 is in use by the kernel driver 'igb'."


NB 'rmmod' is not the ideal approach for PCI assignment. It is better
to explicitly re-bind the device to 'pcistub' because that ensures that
no other driver will ever be able to reclaim the device.

Regards,
Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH] Inform users about busy device assignment attempt
@ 2009-12-09 18:04 Alexander Graf
  2009-12-09 18:19 ` Daniel P. Berrange
  0 siblings, 1 reply; 12+ messages in thread
From: Alexander Graf @ 2009-12-09 18:04 UTC (permalink / raw)
  To: kvm

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.
  *** Try running "rmmod igb" on the commandline
  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 :-).

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 hw/device-assignment.c |   21 +++++++++++++++++++--
 1 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/hw/device-assignment.c b/hw/device-assignment.c
index beb488c..2612c25 100644
--- a/hw/device-assignment.c
+++ b/hw/device-assignment.c
@@ -747,7 +747,8 @@ 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 dir[128], driver[128], *ns;
+    int r, v;
 
     memset(&assigned_dev_data, 0, sizeof(assigned_dev_data));
     assigned_dev_data.assigned_dev_id  =
@@ -769,9 +770,25 @@ static int assign_device(AssignedDevice *dev)
 #endif
 
     r = kvm_assign_pci_device(kvm_context, &assigned_dev_data);
-    if (r < 0)
+    if (r < 0) {
 	fprintf(stderr, "Failed to assign device \"%s\" : %s\n",
                 dev->dev.qdev.id, strerror(-r));
+
+        snprintf(dir, sizeof(dir),
+                 "/sys/bus/pci/devices/0000:%02x:%02x.%x/driver",
+	         dev->host.bus, dev->host.dev, dev->host.func);
+
+        v = readlink(dir, driver, sizeof(driver));
+        if ((v > 0) && (ns = strrchr(driver, '/'))) {
+            ns++;
+            fprintf(stderr, "*** The driver '%s' is occupying your "
+                                 "device %02x:%02x.%x.\n"
+                            "*** Try running \"rmmod %s\" on the command"
+                                 "line\n",
+                            ns, dev->host.bus, dev->host.dev, dev->host.func,
+                            ns);
+        }
+    }
     return r;
 }
 
-- 
1.6.0.2


^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2009-12-15 15:21 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-12-09 20:13 [PATCH] Inform users about busy device assignment attempt Alexander Graf
  -- strict thread matches above, loose matches on Subject: below --
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
2009-12-15 15:18       ` Michael S. Tsirkin
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

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.