Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Staging: usbip: fix multiple interfaces

The stub_probe function instantiates an stub_dev object for all
interfaces. Wich causes a problem. The stub_dev object belongs to their
own interfaces. This patch creates the sdev object at the first
stub_probe call, the other calls associate the interfaces to this.

Signed-off-by: Endre Kollar <taxy443@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Endre Kollar and committed by
Greg Kroah-Hartman
aa5873e9 125ed824

+159 -24
+16 -1
drivers/staging/usbip/stub.h
··· 25 25 #include <linux/module.h> 26 26 #include <linux/net.h> 27 27 28 + #define STUB_BUSID_OTHER 0 29 + #define STUB_BUSID_REMOV 1 30 + #define STUB_BUSID_ADDED 2 31 + #define STUB_BUSID_ALLOC 3 32 + 28 33 struct stub_device { 29 34 struct usb_interface *interface; 30 35 struct list_head list; ··· 77 72 __u32 status; 78 73 }; 79 74 75 + #define BUSID_SIZE 20 76 + struct bus_id_priv { 77 + char name[BUSID_SIZE]; 78 + char status; 79 + int interf_count; 80 + struct stub_device *sdev; 81 + char shutdown_busid; 82 + }; 80 83 81 84 extern struct kmem_cache *stub_priv_cache; 82 85 ··· 104 91 void stub_enqueue_ret_unlink(struct stub_device *, __u32, __u32); 105 92 106 93 /* stub_main.c */ 107 - int match_busid(const char *busid); 94 + struct bus_id_priv *get_busid_priv(const char *busid); 95 + int del_match_busid(char *busid); 96 + 108 97 void stub_device_cleanup_urbs(struct stub_device *sdev);
+91 -10
drivers/staging/usbip/stub_dev.c
··· 393 393 struct stub_device *sdev = NULL; 394 394 const char *udev_busid = dev_name(interface->dev.parent); 395 395 int err = 0; 396 + struct bus_id_priv *busid_priv; 396 397 397 398 dev_dbg(&interface->dev, "Enter\n"); 398 399 399 400 /* check we should claim or not by busid_table */ 400 - if (match_busid(udev_busid)) { 401 + busid_priv = get_busid_priv(udev_busid); 402 + if (!busid_priv || (busid_priv->status == STUB_BUSID_REMOV) || 403 + (busid_priv->status == STUB_BUSID_OTHER)) { 401 404 dev_info(&interface->dev, 402 405 "this device %s is not in match_busid table. skip!\n", 403 406 udev_busid); ··· 425 422 return -ENODEV; 426 423 } 427 424 425 + 426 + if (busid_priv->status == STUB_BUSID_ALLOC) { 427 + busid_priv->interf_count++; 428 + sdev = busid_priv->sdev; 429 + if (!sdev) 430 + return -ENODEV; 431 + 432 + dev_info(&interface->dev, 433 + "USB/IP Stub: register a new interface " 434 + "(bus %u dev %u ifn %u)\n", udev->bus->busnum, udev->devnum, 435 + interface->cur_altsetting->desc.bInterfaceNumber); 436 + 437 + /* set private data to usb_interface */ 438 + usb_set_intfdata(interface, sdev); 439 + 440 + err = stub_add_files(&interface->dev); 441 + if (err) { 442 + dev_err(&interface->dev, "create sysfs files for %s\n", 443 + udev_busid); 444 + usb_set_intfdata(interface, NULL); 445 + busid_priv->interf_count--; 446 + 447 + return err; 448 + } 449 + 450 + return 0; 451 + } 452 + 428 453 /* ok. this is my device. */ 429 454 sdev = stub_device_alloc(interface); 430 455 if (!sdev) 431 456 return -ENOMEM; 432 457 433 - dev_info(&interface->dev, "USB/IP Stub: register a new interface " 458 + dev_info(&interface->dev, "USB/IP Stub: register a new device " 434 459 "(bus %u dev %u ifn %u)\n", udev->bus->busnum, udev->devnum, 435 460 interface->cur_altsetting->desc.bInterfaceNumber); 436 461 462 + busid_priv->interf_count = 0; 463 + busid_priv->shutdown_busid = 0; 464 + 437 465 /* set private data to usb_interface */ 438 466 usb_set_intfdata(interface, sdev); 467 + busid_priv->interf_count++; 468 + 469 + busid_priv->sdev = sdev; 439 470 440 471 err = stub_add_files(&interface->dev); 441 472 if (err) { 442 473 dev_err(&interface->dev, "create sysfs files for %s\n", 443 474 udev_busid); 444 - usb_set_intfdata(interface, 0); 475 + usb_set_intfdata(interface, NULL); 476 + busid_priv->interf_count = 0; 477 + 478 + busid_priv->sdev = NULL; 445 479 stub_device_free(sdev); 446 480 return err; 447 481 } 482 + busid_priv->status = STUB_BUSID_ALLOC; 448 483 449 484 return 0; 485 + } 486 + 487 + static void shutdown_busid(struct bus_id_priv *busid_priv) 488 + { 489 + if (busid_priv->sdev && !busid_priv->shutdown_busid) { 490 + busid_priv->shutdown_busid = 1; 491 + usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED); 492 + 493 + /* 2. wait for the stop of the event handler */ 494 + usbip_stop_eh(&busid_priv->sdev->ud); 495 + } 496 + 450 497 } 451 498 452 499 ··· 506 453 */ 507 454 static void stub_disconnect(struct usb_interface *interface) 508 455 { 509 - struct stub_device *sdev = usb_get_intfdata(interface); 456 + struct stub_device *sdev; 457 + const char *udev_busid = dev_name(interface->dev.parent); 458 + struct bus_id_priv *busid_priv; 459 + 460 + busid_priv = get_busid_priv(udev_busid); 510 461 511 462 usbip_udbg("Enter\n"); 463 + 464 + if (!busid_priv) { 465 + BUG(); 466 + return; 467 + } 468 + 469 + sdev = usb_get_intfdata(interface); 512 470 513 471 /* get stub_device */ 514 472 if (!sdev) { ··· 530 466 531 467 usb_set_intfdata(interface, NULL); 532 468 533 - 534 469 /* 535 470 * NOTE: 536 471 * rx/tx threads are invoked for each usb_device. 537 472 */ 538 473 stub_remove_files(&interface->dev); 539 474 540 - /* 1. shutdown the current connection */ 541 - usbip_event_add(&sdev->ud, SDEV_EVENT_REMOVED); 475 + /*If usb reset called from event handler*/ 476 + if (busid_priv->sdev->ud.eh.thread == current) { 477 + busid_priv->interf_count--; 478 + return; 479 + } 542 480 543 - /* 2. wait for the stop of the event handler */ 544 - usbip_stop_eh(&sdev->ud); 481 + if (busid_priv->interf_count > 1) { 482 + busid_priv->interf_count--; 483 + shutdown_busid(busid_priv); 484 + return; 485 + } 486 + 487 + busid_priv->interf_count = 0; 488 + 489 + 490 + /* 1. shutdown the current connection */ 491 + shutdown_busid(busid_priv); 545 492 546 493 /* 3. free sdev */ 494 + busid_priv->sdev = NULL; 547 495 stub_device_free(sdev); 548 496 549 - 497 + if (busid_priv->status == STUB_BUSID_ALLOC) { 498 + busid_priv->status = STUB_BUSID_ADDED; 499 + } else { 500 + busid_priv->status = STUB_BUSID_OTHER; 501 + del_match_busid((char *)udev_busid); 502 + } 550 503 usbip_udbg("bye\n"); 551 504 }
+52 -13
drivers/staging/usbip/stub_main.c
··· 41 41 * remote host. 42 42 */ 43 43 #define MAX_BUSID 16 44 - #define BUSID_SIZE 20 45 - static char busid_table[MAX_BUSID][BUSID_SIZE]; 44 + static struct bus_id_priv busid_table[MAX_BUSID]; 46 45 static spinlock_t busid_table_lock; 47 46 48 47 ··· 52 53 spin_lock(&busid_table_lock); 53 54 54 55 for (i = 0; i < MAX_BUSID; i++) 55 - if (busid_table[i][0]) 56 - if (!strncmp(busid_table[i], busid, BUSID_SIZE)) { 56 + if (busid_table[i].name[0]) 57 + if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { 57 58 /* already registerd */ 58 59 spin_unlock(&busid_table_lock); 59 60 return 0; ··· 64 65 return 1; 65 66 } 66 67 68 + struct bus_id_priv *get_busid_priv(const char *busid) 69 + { 70 + int i; 71 + 72 + spin_lock(&busid_table_lock); 73 + 74 + for (i = 0; i < MAX_BUSID; i++) 75 + if (busid_table[i].name[0]) 76 + if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { 77 + /* already registerd */ 78 + spin_unlock(&busid_table_lock); 79 + return &(busid_table[i]); 80 + } 81 + 82 + spin_unlock(&busid_table_lock); 83 + 84 + return NULL; 85 + } 86 + 67 87 static ssize_t show_match_busid(struct device_driver *drv, char *buf) 68 88 { 69 89 int i; ··· 91 73 spin_lock(&busid_table_lock); 92 74 93 75 for (i = 0; i < MAX_BUSID; i++) 94 - if (busid_table[i][0]) 95 - out += sprintf(out, "%s ", busid_table[i]); 76 + if (busid_table[i].name[0]) 77 + out += sprintf(out, "%s ", busid_table[i].name); 96 78 97 79 spin_unlock(&busid_table_lock); 98 80 ··· 111 93 spin_lock(&busid_table_lock); 112 94 113 95 for (i = 0; i < MAX_BUSID; i++) 114 - if (!busid_table[i][0]) { 115 - strncpy(busid_table[i], busid, BUSID_SIZE); 96 + if (!busid_table[i].name[0]) { 97 + strncpy(busid_table[i].name, busid, BUSID_SIZE); 98 + if ((busid_table[i].status != STUB_BUSID_ALLOC) && 99 + (busid_table[i].status != STUB_BUSID_REMOV)) 100 + busid_table[i].status = STUB_BUSID_ADDED; 116 101 spin_unlock(&busid_table_lock); 117 102 return 0; 118 103 } ··· 125 104 return -1; 126 105 } 127 106 128 - static int del_match_busid(char *busid) 107 + int del_match_busid(char *busid) 129 108 { 130 109 int i; 131 110 132 111 spin_lock(&busid_table_lock); 133 112 134 113 for (i = 0; i < MAX_BUSID; i++) 135 - if (!strncmp(busid_table[i], busid, BUSID_SIZE)) { 114 + if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { 136 115 /* found */ 137 - memset(busid_table[i], 0, BUSID_SIZE); 116 + if (busid_table[i].status == STUB_BUSID_OTHER) 117 + memset(busid_table[i].name, 0, BUSID_SIZE); 118 + if ((busid_table[i].status != STUB_BUSID_OTHER) && 119 + (busid_table[i].status != STUB_BUSID_ADDED)) { 120 + busid_table[i].status = STUB_BUSID_REMOV; 121 + } 138 122 spin_unlock(&busid_table_lock); 139 123 return 0; 140 124 } ··· 147 121 spin_unlock(&busid_table_lock); 148 122 149 123 return -1; 124 + } 125 + static void init_busid_table(void) 126 + { 127 + int i; 128 + 129 + 130 + for (i = 0; i < MAX_BUSID; i++) { 131 + memset(busid_table[i].name, 0, BUSID_SIZE); 132 + busid_table[i].status = STUB_BUSID_OTHER; 133 + busid_table[i].interf_count = 0; 134 + busid_table[i].sdev = NULL; 135 + busid_table[i].shutdown_busid = 0; 136 + } 137 + spin_lock_init(&busid_table_lock); 150 138 } 151 139 152 140 static ssize_t store_match_busid(struct device_driver *dev, const char *buf, ··· 301 261 printk(KERN_INFO KBUILD_MODNAME ":" 302 262 DRIVER_DESC ":" DRIVER_VERSION "\n"); 303 263 304 - memset(busid_table, 0, sizeof(busid_table)); 305 - spin_lock_init(&busid_table_lock); 264 + init_busid_table(); 306 265 307 266 ret = driver_create_file(&stub_driver.drvwrap.driver, 308 267 &driver_attr_match_busid);