From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Google-Smtp-Source: AH8x2243ordnLOAvRC1FSWYLu+XHBUYMg4rbEOv5HEylHoMreC6PDTqK2MPXv15JiDj3sQtJESd+ ARC-Seal: i=1; a=rsa-sha256; t=1517301438; cv=none; d=google.com; s=arc-20160816; b=bDdoklmI0GaCxVkfoyjrwoiKN5fawbqedXxYgKBnYFcJtn0tXwOhJXXyLneMEdH2Mt GGB3zOKqI2RbzYxvIih2R1U58NcvXGEH4jY1kZSs+odN48BzXEi+ax/KlJLhQge2yWtc byjWwDYngxE5SIbgeoSRe8VDYvjJs9VSzsNCBMmYZlq0fmZ8M1pT667f5PByI+aLYHTT ZZ7s0UHHbIMaaQFgBAfWI6oOj4rqSWKnkGD/SxMlRQWyYb241norhrMRwdgWMSwrtRDV 9cotxgIyCLYWMBm0HuOg9xcdyWEvdNgTZN/Q1VgJMJfjH9/GHSvH4Gaw6KrtcE7mGuXc 472w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:arc-authentication-results; bh=+40nnlZWPvcF/8GYTrweqbQnPcO0raEmaZokIrhOebo=; b=gUKCnx+zMkd4K6uiXvQXgKgy8STTS5ZgQ4SaYzhTRwgEaY9AQLDRSBXCvn+VI3Pl0L fiqDVkljVOUPaBNeqY+o+T7dShAt6hmkkpfIrJ0PMagqF/07vyv5gaE5VTJbJTGze/8x c5+7o6IPulzheCGRe2W+BRKgAXyCah5DFwgbiLo0Q4kEmVPnUsWFAi0OGMGpcdE44JCD zukuNdz/tPkQTPrqeg09XDfQWDCsikAOOrkBAuxQF7PcY3WkYxT+gu5qPMTPIHXRhsDJ QIjCSnFdBJA0hG0Fa1+X2NYtM08vR9Cb/Ttpj731XsTHxA4rdLzfYybqruuaRkmFA6wB z+3Q== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of salva@qindel.com designates 89.140.90.34 as permitted sender) smtp.mailfrom=salva@qindel.com Authentication-Results: mx.google.com; spf=pass (google.com: domain of salva@qindel.com designates 89.140.90.34 as permitted sender) smtp.mailfrom=salva@qindel.com From: Salvador Fandino To: linux-usb@vger.kernel.org Cc: gregkh@linuxfoundation.org, valentina.manea.m@gmail.com, shuah@kernel.org, linux-kernel@vger.kernel.org, =?UTF-8?q?Salvador=20Fandi=C3=B1o?= Subject: [PATCH 1/4] usbip: make vhci_hcd.* objects independent of vhci_hcd.0 Date: Tue, 30 Jan 2018 09:36:27 +0100 Message-Id: <20180130083630.26501-2-salva@qindel.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: <20180130083630.26501-1-salva@qindel.com> References: <20180130083630.26501-1-salva@qindel.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: =?utf-8?q?1591005843418915487?= X-GMAIL-MSGID: =?utf-8?q?1591005873243193653?= X-Mailing-List: linux-kernel@vger.kernel.org List-ID: From: Salvador Fandi=C3=B1o The usbip VHCI layer supports multiple "vhci_hcd" devices, every one emulating both a high speed and a super speed USB hub. These devices are exposed in sysfs as "vhci_hcd.0", "vhci_hcd.1", etc. But instead of controlling then by attributes inside their respective directories, all of then were controlled through "vhci_hcd.0" where the attributes "attach" and "detach" were used to connect and disconnect respectively remote USB devices to any of the virtual hub ports. Also port status was show but a series of "status.X" attributes, all inside "vhci_hcd.0". Besides the rather unusuality of this approach, it precludes users from doing interesting things, as for instance, restricting the access to vhci_hcd devices independently. This patch adds "attach", "detach", "status" and "nports" attributes inside every "vhci_hcd" object, besides "vhci_hcd.0" making then independent. Attribute "debug", allowing to control when debug messages for the usbip subsystems are generated is, as before, only attached to "vhci_hcd.0". Signed-off-by: Salvador Fandi=C3=B1o --- drivers/usb/usbip/vhci.h | 21 ++- drivers/usb/usbip/vhci_hcd.c | 25 ++-- drivers/usb/usbip/vhci_sysfs.c | 311 +++++++++++++----------------------= ------ 3 files changed, 132 insertions(+), 225 deletions(-) diff --git a/drivers/usb/usbip/vhci.h b/drivers/usb/usbip/vhci.h index 5659dce1526e..c1b848775be1 100644 --- a/drivers/usb/usbip/vhci.h +++ b/drivers/usb/usbip/vhci.h @@ -88,7 +88,14 @@ enum hub_speed { #define VHCI_NR_HCS 1 #endif =20 -#define MAX_STATUS_NAME 16 +struct vhci_attrs { + struct dev_ext_attribute dev_attr_status; + struct dev_ext_attribute dev_attr_attach; + struct dev_ext_attribute dev_attr_detach; + struct dev_ext_attribute dev_attr_nports; + + struct attribute_group attribute_group; +}; =20 struct vhci { spinlock_t lock; @@ -97,6 +104,8 @@ struct vhci { =20 struct vhci_hcd *vhci_hcd_hs; struct vhci_hcd *vhci_hcd_ss; + + struct vhci_attrs *attrs; }; =20 /* for usb_hcd.hcd_priv[0] */ @@ -126,8 +135,8 @@ extern struct attribute_group vhci_attr_group; void rh_port_connect(struct vhci_device *vdev, enum usb_device_speed spe= ed); =20 /* vhci_sysfs.c */ -int vhci_init_attr_group(void); -void vhci_finish_attr_group(void); +int vhci_init_attr_group(struct vhci_hcd *vhci_hcd, int id); +void vhci_finish_attr_group(struct vhci_hcd *vhci_hcd); =20 /* vhci_rx.c */ struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seq= num); @@ -171,4 +180,10 @@ static inline struct vhci_hcd *vdev_to_vhci_hcd(stru= ct vhci_device *vdev) return container_of((void *)(vdev - vdev->rhport), struct vhci_hcd, vde= v); } =20 +static inline struct vhci *device_attribute_to_vhci( + struct device_attribute *attr) +{ + return (struct vhci *)((struct dev_ext_attribute *)attr)->var; +} + #endif /* __USBIP_VHCI_H */ diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index c3e1008aa491..daabb06c9f46 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -1110,13 +1110,14 @@ static int vhci_setup(struct usb_hcd *hcd) static int vhci_start(struct usb_hcd *hcd) { struct vhci_hcd *vhci_hcd =3D hcd_to_vhci_hcd(hcd); + struct vhci *vhci =3D vhci_hcd->vhci; int id, rhport; int err; =20 - usbip_dbg_vhci_hc("enter vhci_start\n"); + usbip_dbg_vhci_hc("enter vhci_start for %s\n", hcd_name(hcd)); =20 if (usb_hcd_is_primary_hcd(hcd)) - spin_lock_init(&vhci_hcd->vhci->lock); + spin_lock_init(&vhci->lock); =20 /* initialize private data of usb_hcd */ =20 @@ -1143,16 +1144,17 @@ static int vhci_start(struct usb_hcd *hcd) } =20 /* vhci_hcd is now ready to be controlled through sysfs */ - if (id =3D=3D 0 && usb_hcd_is_primary_hcd(hcd)) { - err =3D vhci_init_attr_group(); + if (usb_hcd_is_primary_hcd(hcd)) { + err =3D vhci_init_attr_group(vhci_hcd, id); if (err) { pr_err("init attr group\n"); return err; } - err =3D sysfs_create_group(&hcd_dev(hcd)->kobj, &vhci_attr_group); + err =3D sysfs_create_group(&hcd_dev(hcd)->kobj, + &vhci->attrs->attribute_group); if (err) { pr_err("create sysfs files\n"); - vhci_finish_attr_group(); + vhci_finish_attr_group(vhci_hcd); return err; } pr_info("created sysfs %s\n", hcd_name(hcd)); @@ -1164,15 +1166,16 @@ static int vhci_start(struct usb_hcd *hcd) static void vhci_stop(struct usb_hcd *hcd) { struct vhci_hcd *vhci_hcd =3D hcd_to_vhci_hcd(hcd); - int id, rhport; + struct vhci *vhci =3D vhci_hcd->vhci; + int rhport; =20 usbip_dbg_vhci_hc("stop VHCI controller\n"); =20 /* 1. remove the userland interface of vhci_hcd */ - id =3D hcd_name_to_id(hcd_name(hcd)); - if (id =3D=3D 0 && usb_hcd_is_primary_hcd(hcd)) { - sysfs_remove_group(&hcd_dev(hcd)->kobj, &vhci_attr_group); - vhci_finish_attr_group(); + if (usb_hcd_is_primary_hcd(hcd)) { + sysfs_remove_group(&hcd_dev(hcd)->kobj, + &vhci->attrs->attribute_group); + vhci_finish_attr_group(vhci_hcd); } =20 /* 2. shutdown all the ports of vhci_hcd */ diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysf= s.c index 091f76b7196d..9e296ee9b9cb 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -57,24 +57,14 @@ static void port_show_vhci(char **out, int hub, int p= ort, struct vhci_device *vd } =20 /* Sysfs entry to show port status */ -static ssize_t status_show_vhci(int pdev_nr, char *out) +static ssize_t status_show_vhci(struct vhci *vhci, char *out) { - struct platform_device *pdev =3D vhcis[pdev_nr].pdev; - struct vhci *vhci; - struct usb_hcd *hcd; - struct vhci_hcd *vhci_hcd; char *s =3D out; int i; unsigned long flags; =20 - if (!pdev || !out) { - usbip_dbg_vhci_sysfs("show status error\n"); + if (WARN_ON(!vhci) || WARN_ON(!out)) return 0; - } - - hcd =3D platform_get_drvdata(pdev); - vhci_hcd =3D hcd_to_vhci_hcd(hcd); - vhci =3D vhci_hcd->vhci; =20 spin_lock_irqsave(&vhci->lock, flags); =20 @@ -83,7 +73,7 @@ static ssize_t status_show_vhci(int pdev_nr, char *out) =20 spin_lock(&vdev->ud.lock); port_show_vhci(&out, HUB_SPEED_HIGH, - pdev_nr * VHCI_PORTS + i, vdev); + i, vdev); spin_unlock(&vdev->ud.lock); } =20 @@ -92,7 +82,7 @@ static ssize_t status_show_vhci(int pdev_nr, char *out) =20 spin_lock(&vdev->ud.lock); port_show_vhci(&out, HUB_SPEED_SUPER, - pdev_nr * VHCI_PORTS + VHCI_HC_PORTS + i, vdev); + VHCI_HC_PORTS + i, vdev); spin_unlock(&vdev->ud.lock); } =20 @@ -101,77 +91,21 @@ static ssize_t status_show_vhci(int pdev_nr, char *o= ut) return out - s; } =20 -static ssize_t status_show_not_ready(int pdev_nr, char *out) -{ - char *s =3D out; - int i =3D 0; - - for (i =3D 0; i < VHCI_HC_PORTS; i++) { - out +=3D sprintf(out, "hs %04u %03u ", - (pdev_nr * VHCI_PORTS) + i, - VDEV_ST_NOTASSIGNED); - out +=3D sprintf(out, "000 00000000 0000000000000000 0-0"); - out +=3D sprintf(out, "\n"); - } - - for (i =3D 0; i < VHCI_HC_PORTS; i++) { - out +=3D sprintf(out, "ss %04u %03u ", - (pdev_nr * VHCI_PORTS) + VHCI_HC_PORTS + i, - VDEV_ST_NOTASSIGNED); - out +=3D sprintf(out, "000 00000000 0000000000000000 0-0"); - out +=3D sprintf(out, "\n"); - } - return out - s; -} - -static int status_name_to_id(const char *name) -{ - char *c; - long val; - int ret; - - c =3D strchr(name, '.'); - if (c =3D=3D NULL) - return 0; - - ret =3D kstrtol(c+1, 10, &val); - if (ret < 0) - return ret; - - return val; -} - static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *out) { char *s =3D out; - int pdev_nr; - out +=3D sprintf(out, "hub port sta spd dev socket local_busid\n"); - - pdev_nr =3D status_name_to_id(attr->attr.name); - if (pdev_nr < 0) - out +=3D status_show_not_ready(pdev_nr, out); - else - out +=3D status_show_vhci(pdev_nr, out); - + out +=3D status_show_vhci(device_attribute_to_vhci(attr), out); return out - s; } =20 static ssize_t nports_show(struct device *dev, struct device_attribute *= attr, char *out) { - char *s =3D out; - - /* - * Half the ports are for SPEED_HIGH and half for SPEED_SUPER, - * thus the * 2. - */ - out +=3D sprintf(out, "%d\n", VHCI_PORTS * vhci_num_controllers); - return out - s; + return sprintf(out, "%d\n", VHCI_PORTS); } -static DEVICE_ATTR_RO(nports); =20 /* Sysfs entry to shutdown a virtual connection */ static int vhci_port_disconnect(struct vhci_hcd *vhci_hcd, __u32 rhport) @@ -205,14 +139,11 @@ static int vhci_port_disconnect(struct vhci_hcd *vh= ci_hcd, __u32 rhport) return 0; } =20 -static int valid_port(__u32 pdev_nr, __u32 rhport) +static int validate_port_in_range(__u32 port, __u32 base, __u32 top) { - if (pdev_nr >=3D vhci_num_controllers) { - pr_err("pdev %u\n", pdev_nr); - return 0; - } - if (rhport >=3D VHCI_HC_PORTS) { - pr_err("rhport %u\n", rhport); + if (port < base || port >=3D top) { + pr_err("Port number %u outside of range [%u-%u]\n", + port, base, top - 1); return 0; } return 1; @@ -221,34 +152,24 @@ static int valid_port(__u32 pdev_nr, __u32 rhport) static ssize_t store_detach(struct device *dev, struct device_attribute = *attr, const char *buf, size_t count) { - __u32 port =3D 0, pdev_nr =3D 0, rhport =3D 0; - struct usb_hcd *hcd; - struct vhci_hcd *vhci_hcd; + struct vhci *vhci =3D device_attribute_to_vhci(attr); + __u32 port =3D 0; int ret; =20 if (kstrtoint(buf, 10, &port) < 0) return -EINVAL; =20 - pdev_nr =3D port_to_pdev_nr(port); - rhport =3D port_to_rhport(port); + usbip_dbg_vhci_sysfs("%s: detach port %d\n", dev_name(dev), port); =20 - if (!valid_port(pdev_nr, rhport)) + if (!validate_port_in_range(port, 0, VHCI_PORTS)) return -EINVAL; =20 - hcd =3D platform_get_drvdata(vhcis[pdev_nr].pdev); - if (hcd =3D=3D NULL) { - dev_err(dev, "port is not ready %u\n", port); - return -EAGAIN; - } - - usbip_dbg_vhci_sysfs("rhport %d\n", rhport); - - if ((port / VHCI_HC_PORTS) % 2) - vhci_hcd =3D hcd_to_vhci_hcd(hcd)->vhci->vhci_hcd_ss; + if (port >=3D VHCI_HC_PORTS) + ret =3D vhci_port_disconnect(vhci->vhci_hcd_ss, + port - VHCI_HC_PORTS); else - vhci_hcd =3D hcd_to_vhci_hcd(hcd)->vhci->vhci_hcd_hs; + ret =3D vhci_port_disconnect(vhci->vhci_hcd_hs, port); =20 - ret =3D vhci_port_disconnect(vhci_hcd, rhport); if (ret < 0) return -EINVAL; =20 @@ -256,29 +177,6 @@ static ssize_t store_detach(struct device *dev, stru= ct device_attribute *attr, =20 return count; } -static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach); - -static int valid_args(__u32 pdev_nr, __u32 rhport, enum usb_device_speed= speed) -{ - if (!valid_port(pdev_nr, rhport)) { - return 0; - } - - switch (speed) { - case USB_SPEED_LOW: - case USB_SPEED_FULL: - case USB_SPEED_HIGH: - case USB_SPEED_WIRELESS: - case USB_SPEED_SUPER: - break; - default: - pr_err("Failed attach request for unsupported USB speed: %s\n", - usb_speed_string(speed)); - return 0; - } - - return 1; -} =20 /* Sysfs entry to establish a virtual connection */ /* @@ -295,50 +193,47 @@ static int valid_args(__u32 pdev_nr, __u32 rhport, = enum usb_device_speed speed) static ssize_t store_attach(struct device *dev, struct device_attribute = *attr, const char *buf, size_t count) { + struct vhci *vhci =3D device_attribute_to_vhci(attr); struct socket *socket; int sockfd =3D 0; - __u32 port =3D 0, pdev_nr =3D 0, rhport =3D 0, devid =3D 0, speed =3D 0= ; - struct usb_hcd *hcd; - struct vhci_hcd *vhci_hcd; + __u32 port =3D 0, devid =3D 0, speed =3D 0; struct vhci_device *vdev; - struct vhci *vhci; int err; unsigned long flags; =20 /* - * @rhport: port number of vhci_hcd + * @port: port number of vhci_hcd * @sockfd: socket descriptor of an established TCP connection * @devid: unique device identifier in a remote host * @speed: usb device speed in a remote host */ if (sscanf(buf, "%u %u %u %u", &port, &sockfd, &devid, &speed) !=3D 4) return -EINVAL; - pdev_nr =3D port_to_pdev_nr(port); - rhport =3D port_to_rhport(port); =20 - usbip_dbg_vhci_sysfs("port(%u) pdev(%d) rhport(%u)\n", - port, pdev_nr, rhport); - usbip_dbg_vhci_sysfs("sockfd(%u) devid(%u) speed(%u)\n", - sockfd, devid, speed); + usbip_dbg_vhci_sysfs("%s: attach port(%u) sockfd(%u) devid(%u) speed(%u= )\n", + dev_name(dev), port, sockfd, devid, speed); =20 /* check received parameters */ - if (!valid_args(pdev_nr, rhport, speed)) + switch (speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + case USB_SPEED_WIRELESS: + if (!validate_port_in_range(port, 0, VHCI_HC_PORTS)) + return -EINVAL; + vdev =3D &vhci->vhci_hcd_hs->vdev[port]; + break; + case USB_SPEED_SUPER: + if (!validate_port_in_range(port, VHCI_HC_PORTS, VHCI_PORTS)) + return -EINVAL; + vdev =3D &vhci->vhci_hcd_ss->vdev[port - VHCI_HC_PORTS]; + break; + default: + pr_err("Failed attach request for unsupported USB speed: %s\n", + usb_speed_string(speed)); return -EINVAL; - - hcd =3D platform_get_drvdata(vhcis[pdev_nr].pdev); - if (hcd =3D=3D NULL) { - dev_err(dev, "port %d is not ready\n", port); - return -EAGAIN; } =20 - vhci_hcd =3D hcd_to_vhci_hcd(hcd); - vhci =3D vhci_hcd->vhci; - - if (speed =3D=3D USB_SPEED_SUPER) - vdev =3D &vhci->vhci_hcd_ss->vdev[rhport]; - else - vdev =3D &vhci->vhci_hcd_hs->vdev[rhport]; - /* Extract socket from fd. */ socket =3D sockfd_lookup(sockfd, &err); if (!socket) @@ -357,7 +252,7 @@ static ssize_t store_attach(struct device *dev, struc= t device_attribute *attr, =20 sockfd_put(socket); =20 - dev_err(dev, "port %d already used\n", rhport); + dev_err(dev, "port %u already used\n", port); /* * Will be retried from userspace * if there's another free port. @@ -365,8 +260,8 @@ static ssize_t store_attach(struct device *dev, struc= t device_attribute *attr, return -EBUSY; } =20 - dev_info(dev, "pdev(%u) rhport(%u) sockfd(%d)\n", - pdev_nr, rhport, sockfd); + dev_info(dev, "port(%u) sockfd(%d)\n", + port, sockfd); dev_info(dev, "devid(%u) speed(%u) speed_str(%s)\n", devid, speed, usb_speed_string(speed)); =20 @@ -387,83 +282,77 @@ static ssize_t store_attach(struct device *dev, str= uct device_attribute *attr, =20 return count; } -static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach); =20 -#define MAX_STATUS_NAME 16 - -struct status_attr { - struct device_attribute attr; - char name[MAX_STATUS_NAME+1]; -}; - -static struct status_attr *status_attrs; - -static void set_status_attr(int id) +int vhci_init_attr_group(struct vhci_hcd *vhci_hcd, int id) { - struct status_attr *status; + struct vhci_attrs *vhci_attrs; + struct attribute **attrs; + struct vhci *vhci =3D vhci_hcd->vhci; + int nattrs =3D ((id =3D=3D 0) ? 5 : 4); =20 - status =3D status_attrs + id; - if (id =3D=3D 0) - strcpy(status->name, "status"); - else - snprintf(status->name, MAX_STATUS_NAME+1, "status.%d", id); - status->attr.attr.name =3D status->name; - status->attr.attr.mode =3D S_IRUGO; - status->attr.show =3D status_show; - sysfs_attr_init(&status->attr.attr); -} + if (WARN_ON(vhci->attrs !=3D NULL)) + return -EADDRINUSE; =20 -static int init_status_attrs(void) -{ - int id; + vhci_attrs =3D kcalloc(1, sizeof(*vhci_attrs), GFP_KERNEL); + if (vhci_attrs =3D=3D NULL) + return -ENOMEM; =20 - status_attrs =3D kcalloc(vhci_num_controllers, sizeof(struct status_att= r), - GFP_KERNEL); - if (status_attrs =3D=3D NULL) + attrs =3D kmalloc_array(nattrs + 1, sizeof(*attrs), GFP_KERNEL); + if (attrs =3D=3D NULL) { + kfree(vhci_attrs); return -ENOMEM; + } =20 - for (id =3D 0; id < vhci_num_controllers; id++) - set_status_attr(id); + vhci->attrs =3D vhci_attrs; + vhci_attrs->attribute_group.attrs =3D attrs; + + vhci_attrs->dev_attr_status.attr.attr.name =3D "status"; + vhci_attrs->dev_attr_status.attr.attr.mode =3D 0444; + vhci_attrs->dev_attr_status.attr.show =3D status_show; + vhci_attrs->dev_attr_status.var =3D vhci; + sysfs_attr_init(&vhci_attrs->dev_attr_status.attr.attr); + attrs[0] =3D &vhci_attrs->dev_attr_status.attr.attr; + + vhci_attrs->dev_attr_attach.attr.attr.name =3D "attach"; + vhci_attrs->dev_attr_attach.attr.attr.mode =3D 0200; + vhci_attrs->dev_attr_attach.attr.store =3D store_attach; + vhci_attrs->dev_attr_attach.var =3D vhci; + sysfs_attr_init(&vhci_attrs->dev_attr_attach.attr.attr); + attrs[1] =3D &vhci_attrs->dev_attr_attach.attr.attr; + + vhci_attrs->dev_attr_detach.attr.attr.name =3D "detach"; + vhci_attrs->dev_attr_detach.attr.attr.mode =3D 0200; + vhci_attrs->dev_attr_detach.attr.store =3D store_detach; + vhci_attrs->dev_attr_detach.var =3D vhci; + sysfs_attr_init(&vhci_attrs->dev_attr_detach.attr.attr); + attrs[2] =3D &vhci_attrs->dev_attr_detach.attr.attr; + + vhci_attrs->dev_attr_nports.attr.attr.name =3D "nports"; + vhci_attrs->dev_attr_nports.attr.attr.mode =3D 0444; + vhci_attrs->dev_attr_nports.attr.show =3D nports_show; + vhci_attrs->dev_attr_nports.var =3D vhci; + sysfs_attr_init(&vhci_attrs->dev_attr_nports.attr.attr); + attrs[3] =3D &vhci_attrs->dev_attr_nports.attr.attr; + + if (id =3D=3D 0) { + attrs[4] =3D &dev_attr_usbip_debug.attr; + attrs[5] =3D NULL; + } else { + attrs[4] =3D NULL; + } =20 return 0; } =20 -static void finish_status_attrs(void) +void vhci_finish_attr_group(struct vhci_hcd *vhci_hcd) { - kfree(status_attrs); -} - -struct attribute_group vhci_attr_group =3D { - .attrs =3D NULL, -}; + struct vhci_attrs *vhci_attrs =3D vhci_hcd->vhci->attrs; =20 -int vhci_init_attr_group(void) -{ - struct attribute **attrs; - int ret, i; + if (vhci_attrs) { + struct attribute **attrs =3D vhci_attrs->attribute_group.attrs; =20 - attrs =3D kcalloc((vhci_num_controllers + 5), sizeof(struct attribute *= ), - GFP_KERNEL); - if (attrs =3D=3D NULL) - return -ENOMEM; - - ret =3D init_status_attrs(); - if (ret) { kfree(attrs); - return ret; + kfree(vhci_attrs); + vhci_hcd->vhci->attrs =3D NULL; } - *attrs =3D &dev_attr_nports.attr; - *(attrs + 1) =3D &dev_attr_detach.attr; - *(attrs + 2) =3D &dev_attr_attach.attr; - *(attrs + 3) =3D &dev_attr_usbip_debug.attr; - for (i =3D 0; i < vhci_num_controllers; i++) - *(attrs + i + 4) =3D &((status_attrs + i)->attr.attr); - vhci_attr_group.attrs =3D attrs; - return 0; -} - -void vhci_finish_attr_group(void) -{ - finish_status_attrs(); - kfree(vhci_attr_group.attrs); } --=20 2.14.1