usb: xhci-mtk: fix unreleased bandwidth data
authorIkjoon Jang <ikjn@chromium.org>
Wed, 13 Jan 2021 10:05:11 +0000 (18:05 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 26 Jan 2021 14:54:04 +0000 (15:54 +0100)
xhci-mtk needs XHCI_MTK_HOST quirk functions in add_endpoint() and
drop_endpoint() to handle its own sw bandwidth management.

It stores bandwidth data into an internal table every time
add_endpoint() is called, and drops those in drop_endpoint().
But when bandwidth allocation fails at one endpoint, all earlier
allocation from the same interface could still remain at the table.

This patch moves bandwidth management codes to check_bandwidth() and
reset_bandwidth() path. To do so, this patch also adds those functions
to xhci_driver_overrides and lets mtk-xhci to release all failed
endpoints in reset_bandwidth() path.

Fixes: 08e469de87a2 ("usb: xhci-mtk: supports bandwidth scheduling with multi-TT")
Signed-off-by: Ikjoon Jang <ikjn@chromium.org>
Link: https://lore.kernel.org/r/20210113180444.v6.1.Id0d31b5f3ddf5e734d2ab11161ac5821921b1e1e@changeid
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/xhci-mtk-sch.c
drivers/usb/host/xhci-mtk.c
drivers/usb/host/xhci-mtk.h
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h

index 45c54d5..a313e75 100644 (file)
@@ -200,6 +200,7 @@ static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev,
 
        sch_ep->sch_tt = tt;
        sch_ep->ep = ep;
+       INIT_LIST_HEAD(&sch_ep->tt_endpoint);
 
        return sch_ep;
 }
@@ -583,6 +584,8 @@ int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk)
 
        mtk->sch_array = sch_array;
 
+       INIT_LIST_HEAD(&mtk->bw_ep_list_new);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(xhci_mtk_sch_init);
@@ -601,19 +604,14 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
        struct xhci_ep_ctx *ep_ctx;
        struct xhci_slot_ctx *slot_ctx;
        struct xhci_virt_device *virt_dev;
-       struct mu3h_sch_bw_info *sch_bw;
        struct mu3h_sch_ep_info *sch_ep;
-       struct mu3h_sch_bw_info *sch_array;
        unsigned int ep_index;
-       int bw_index;
-       int ret = 0;
 
        xhci = hcd_to_xhci(hcd);
        virt_dev = xhci->devs[udev->slot_id];
        ep_index = xhci_get_endpoint_index(&ep->desc);
        slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx);
        ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
-       sch_array = mtk->sch_array;
 
        xhci_dbg(xhci, "%s() type:%d, speed:%d, mpkt:%d, dir:%d, ep:%p\n",
                __func__, usb_endpoint_type(&ep->desc), udev->speed,
@@ -632,39 +630,34 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
                return 0;
        }
 
-       bw_index = get_bw_index(xhci, udev, ep);
-       sch_bw = &sch_array[bw_index];
-
        sch_ep = create_sch_ep(udev, ep, ep_ctx);
        if (IS_ERR_OR_NULL(sch_ep))
                return -ENOMEM;
 
        setup_sch_info(udev, ep_ctx, sch_ep);
 
-       ret = check_sch_bw(udev, sch_bw, sch_ep);
-       if (ret) {
-               xhci_err(xhci, "Not enough bandwidth!\n");
-               if (is_fs_or_ls(udev->speed))
-                       drop_tt(udev);
-
-               kfree(sch_ep);
-               return -ENOSPC;
-       }
+       list_add_tail(&sch_ep->endpoint, &mtk->bw_ep_list_new);
 
-       list_add_tail(&sch_ep->endpoint, &sch_bw->bw_ep_list);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(xhci_mtk_add_ep_quirk);
 
-       ep_ctx->reserved[0] |= cpu_to_le32(EP_BPKTS(sch_ep->pkts)
-               | EP_BCSCOUNT(sch_ep->cs_count) | EP_BBM(sch_ep->burst_mode));
-       ep_ctx->reserved[1] |= cpu_to_le32(EP_BOFFSET(sch_ep->offset)
-               | EP_BREPEAT(sch_ep->repeat));
+static void xhci_mtk_drop_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev,
+                            struct mu3h_sch_ep_info *sch_ep)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(mtk->hcd);
+       int bw_index = get_bw_index(xhci, udev, sch_ep->ep);
+       struct mu3h_sch_bw_info *sch_bw = &mtk->sch_array[bw_index];
 
-       xhci_dbg(xhci, " PKTS:%x, CSCOUNT:%x, BM:%x, OFFSET:%x, REPEAT:%x\n",
-                       sch_ep->pkts, sch_ep->cs_count, sch_ep->burst_mode,
-                       sch_ep->offset, sch_ep->repeat);
+       update_bus_bw(sch_bw, sch_ep, 0);
+       list_del(&sch_ep->endpoint);
 
-       return 0;
+       if (sch_ep->sch_tt) {
+               list_del(&sch_ep->tt_endpoint);
+               drop_tt(udev);
+       }
+       kfree(sch_ep);
 }
-EXPORT_SYMBOL_GPL(xhci_mtk_add_ep_quirk);
 
 void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
                struct usb_host_endpoint *ep)
@@ -675,7 +668,7 @@ void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
        struct xhci_virt_device *virt_dev;
        struct mu3h_sch_bw_info *sch_array;
        struct mu3h_sch_bw_info *sch_bw;
-       struct mu3h_sch_ep_info *sch_ep;
+       struct mu3h_sch_ep_info *sch_ep, *tmp;
        int bw_index;
 
        xhci = hcd_to_xhci(hcd);
@@ -694,17 +687,73 @@ void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
        bw_index = get_bw_index(xhci, udev, ep);
        sch_bw = &sch_array[bw_index];
 
-       list_for_each_entry(sch_ep, &sch_bw->bw_ep_list, endpoint) {
+       list_for_each_entry_safe(sch_ep, tmp, &sch_bw->bw_ep_list, endpoint) {
                if (sch_ep->ep == ep) {
-                       update_bus_bw(sch_bw, sch_ep, 0);
-                       list_del(&sch_ep->endpoint);
-                       if (is_fs_or_ls(udev->speed)) {
-                               list_del(&sch_ep->tt_endpoint);
-                               drop_tt(udev);
-                       }
-                       kfree(sch_ep);
-                       break;
+                       xhci_mtk_drop_ep(mtk, udev, sch_ep);
                }
        }
 }
 EXPORT_SYMBOL_GPL(xhci_mtk_drop_ep_quirk);
+
+int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
+{
+       struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id];
+       struct mu3h_sch_bw_info *sch_bw;
+       struct mu3h_sch_ep_info *sch_ep, *tmp;
+       int bw_index, ret;
+
+       dev_dbg(&udev->dev, "%s\n", __func__);
+
+       list_for_each_entry(sch_ep, &mtk->bw_ep_list_new, endpoint) {
+               bw_index = get_bw_index(xhci, udev, sch_ep->ep);
+               sch_bw = &mtk->sch_array[bw_index];
+
+               ret = check_sch_bw(udev, sch_bw, sch_ep);
+               if (ret) {
+                       xhci_err(xhci, "Not enough bandwidth!\n");
+                       return -ENOSPC;
+               }
+       }
+
+       list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_list_new, endpoint) {
+               struct xhci_ep_ctx *ep_ctx;
+               struct usb_host_endpoint *ep = sch_ep->ep;
+               unsigned int ep_index = xhci_get_endpoint_index(&ep->desc);
+
+               bw_index = get_bw_index(xhci, udev, ep);
+               sch_bw = &mtk->sch_array[bw_index];
+
+               list_move_tail(&sch_ep->endpoint, &sch_bw->bw_ep_list);
+
+               ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index);
+               ep_ctx->reserved[0] |= cpu_to_le32(EP_BPKTS(sch_ep->pkts)
+                       | EP_BCSCOUNT(sch_ep->cs_count)
+                       | EP_BBM(sch_ep->burst_mode));
+               ep_ctx->reserved[1] |= cpu_to_le32(EP_BOFFSET(sch_ep->offset)
+                       | EP_BREPEAT(sch_ep->repeat));
+
+               xhci_dbg(xhci, " PKTS:%x, CSCOUNT:%x, BM:%x, OFFSET:%x, REPEAT:%x\n",
+                       sch_ep->pkts, sch_ep->cs_count, sch_ep->burst_mode,
+                       sch_ep->offset, sch_ep->repeat);
+       }
+
+       return xhci_check_bandwidth(hcd, udev);
+}
+EXPORT_SYMBOL_GPL(xhci_mtk_check_bandwidth);
+
+void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
+{
+       struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
+       struct mu3h_sch_ep_info *sch_ep, *tmp;
+
+       dev_dbg(&udev->dev, "%s\n", __func__);
+
+       list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_list_new, endpoint) {
+               xhci_mtk_drop_ep(mtk, udev, sch_ep);
+       }
+
+       xhci_reset_bandwidth(hcd, udev);
+}
+EXPORT_SYMBOL_GPL(xhci_mtk_reset_bandwidth);
index 8f321f3..fe010cc 100644 (file)
@@ -347,6 +347,8 @@ static void usb_wakeup_set(struct xhci_hcd_mtk *mtk, bool enable)
 static int xhci_mtk_setup(struct usb_hcd *hcd);
 static const struct xhci_driver_overrides xhci_mtk_overrides __initconst = {
        .reset = xhci_mtk_setup,
+       .check_bandwidth = xhci_mtk_check_bandwidth,
+       .reset_bandwidth = xhci_mtk_reset_bandwidth,
 };
 
 static struct hc_driver __read_mostly xhci_mtk_hc_driver;
index a93cfe8..577f431 100644 (file)
@@ -130,6 +130,7 @@ struct mu3c_ippc_regs {
 struct xhci_hcd_mtk {
        struct device *dev;
        struct usb_hcd *hcd;
+       struct list_head bw_ep_list_new;
        struct mu3h_sch_bw_info *sch_array;
        struct mu3c_ippc_regs __iomem *ippc_regs;
        bool has_ippc;
@@ -166,6 +167,8 @@ int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
                struct usb_host_endpoint *ep);
 void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev,
                struct usb_host_endpoint *ep);
+int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
+void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
 
 #else
 static inline int xhci_mtk_add_ep_quirk(struct usb_hcd *hcd,
@@ -179,6 +182,16 @@ static inline void xhci_mtk_drop_ep_quirk(struct usb_hcd *hcd,
 {
 }
 
+static inline int xhci_mtk_check_bandwidth(struct usb_hcd *hcd,
+               struct usb_device *udev)
+{
+       return 0;
+}
+
+static inline void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd,
+               struct usb_device *udev)
+{
+}
 #endif
 
 #endif         /* _XHCI_MTK_H_ */
index e869405..345a221 100644 (file)
@@ -2985,7 +2985,7 @@ static void xhci_check_bw_drop_ep_streams(struct xhci_hcd *xhci,
  * else should be touching the xhci->devs[slot_id] structure, so we
  * don't need to take the xhci->lock for manipulating that.
  */
-static int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
+int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
 {
        int i;
        int ret = 0;
@@ -3083,7 +3083,7 @@ command_cleanup:
        return ret;
 }
 
-static void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
+void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
 {
        struct xhci_hcd *xhci;
        struct xhci_virt_device *virt_dev;
@@ -5510,6 +5510,10 @@ void xhci_init_driver(struct hc_driver *drv,
                        drv->reset = over->reset;
                if (over->start)
                        drv->start = over->start;
+               if (over->check_bandwidth)
+                       drv->check_bandwidth = over->check_bandwidth;
+               if (over->reset_bandwidth)
+                       drv->reset_bandwidth = over->reset_bandwidth;
        }
 }
 EXPORT_SYMBOL_GPL(xhci_init_driver);
index 25e57bc..07ff950 100644 (file)
@@ -1920,6 +1920,8 @@ struct xhci_driver_overrides {
        size_t extra_priv_size;
        int (*reset)(struct usb_hcd *hcd);
        int (*start)(struct usb_hcd *hcd);
+       int (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
+       void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
 };
 
 #define        XHCI_CFC_DELAY          10
@@ -2074,6 +2076,8 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks);
 void xhci_shutdown(struct usb_hcd *hcd);
 void xhci_init_driver(struct hc_driver *drv,
                      const struct xhci_driver_overrides *over);
+int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
+void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
 int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id);
 int xhci_ext_cap_init(struct xhci_hcd *xhci);