DM USB: dynamically allocate block count for struct usb_xfer

The block count of the struct usb_xfer is hard coded by the macro
USB_MAX_XFER_BLOCKS (1024), it wastes memory if 1024 blocks are
allocated for low speed transfer such as control transfer or interrupt
transfer. This patch introduces a new method to allocate different
number of blocks according to different endpoint type.

Tracked-On: #3628
Signed-off-by: Xiaoguang Wu <xiaoguang.wu@intel.com>
Acked-by: Yu Wang <yu1.wang@intel.com>
This commit is contained in:
Xiaoguang Wu 2019-08-27 22:28:18 +08:00 committed by wenlingz
parent f1b142e6e0
commit 06781b37e9
6 changed files with 95 additions and 42 deletions

View File

@ -1554,19 +1554,66 @@ pci_xhci_deassert_interrupt(struct pci_xhci_vdev *xdev)
static struct usb_xfer *
pci_xhci_alloc_usb_xfer(struct pci_xhci_dev_emu *dev, int epid)
{
int i;
struct usb_xfer *xfer;
struct xhci_dev_ctx *dev_ctx;
struct xhci_endp_ctx *ep_ctx;
int dir, max_blk_cnt, i = 0;
uint8_t type;
if (!dev)
return NULL;
dev_ctx = dev->dev_ctx;
ep_ctx = &dev_ctx->ctx_ep[epid];
type = XHCI_EPCTX_1_EPTYPE_GET(ep_ctx->dwEpCtx1);
/* TODO:
* The following code is still not perfect, due to fixed values are
* not flexible and the overflow risk is still existed. Will try to
* find a dynamic way could work both for Linux and Windows.
*/
switch (type) {
case XHCI_EPTYPE_CTRL:
case XHCI_EPTYPE_INT_IN:
case XHCI_EPTYPE_INT_OUT:
max_blk_cnt = 128;
break;
case XHCI_EPTYPE_BULK_IN:
case XHCI_EPTYPE_BULK_OUT:
max_blk_cnt = 1024;
break;
case XHCI_EPTYPE_ISOC_IN:
case XHCI_EPTYPE_ISOC_OUT:
max_blk_cnt = 2048;
break;
default:
UPRINTF(LFTL, "err: unexpected epid %d type %d dir %d\r\n",
epid, type, dir);
return NULL;
}
xfer = calloc(1, sizeof(struct usb_xfer));
if (!xfer)
return NULL;
for (i = 0; i < USB_MAX_XFER_BLOCKS; ++i) {
xfer->reqs = calloc(max_blk_cnt, sizeof(struct usb_dev_req *));
if (!xfer->reqs)
goto fail;
xfer->data = calloc(max_blk_cnt, sizeof(struct usb_block));
if (!xfer->data)
goto fail;
for (i = 0; i < max_blk_cnt; ++i) {
xfer->data[i].hcb = calloc(1, sizeof(struct xhci_block));
if (!xfer->data[i].hcb)
goto fail;
}
UPRINTF(LINF, "allocate %d blocks for epid %d type %d dir %d\r\n",
max_blk_cnt, epid, type, dir);
xfer->max_blk_cnt = max_blk_cnt;
xfer->dev = (void *)dev;
xfer->epid = epid;
return xfer;
@ -1574,6 +1621,9 @@ pci_xhci_alloc_usb_xfer(struct pci_xhci_dev_emu *dev, int epid)
fail:
for (; i >= 0; i--)
free(xfer->data[i].hcb);
free(xfer->data);
free(xfer->reqs);
free(xfer);
return NULL;
}
@ -1586,15 +1636,12 @@ pci_xhci_free_usb_xfer(struct usb_xfer *xfer)
if (!xfer)
return;
for (i = 0; i < USB_MAX_XFER_BLOCKS; ++i) {
for (i = 0; i < xfer->max_blk_cnt; i++)
free(xfer->data[i].hcb);
xfer->data[i].hcb = NULL;
}
if (xfer->ureq) {
free(xfer->ureq);
xfer->ureq = NULL;
}
free(xfer->data);
free(xfer->reqs);
free(xfer->ureq);
free(xfer);
}
@ -2292,8 +2339,8 @@ pci_xhci_cmd_reset_ep(struct pci_xhci_vdev *xdev,
pthread_mutex_lock(&devep->mtx);
xfer = devep->ep_xfer;
for (i = 0; i < USB_MAX_XFER_BLOCKS; ++i) {
r = xfer->requests[i];
for (i = 0; i < xfer->max_blk_cnt; ++i) {
r = xfer->reqs[i];
if (r && r->trn)
/* let usb_dev_comp_req to free the memory */
libusb_cancel_transfer(r->trn);
@ -2744,13 +2791,13 @@ pci_xhci_xfer_complete(struct pci_xhci_vdev *xdev, struct usb_xfer *xfer,
xfer->data[i].stat = USB_BLOCK_FREE;
xfer->ndata--;
xfer->head = index_inc(xfer->head, USB_MAX_XFER_BLOCKS);
xfer->head = index_inc(xfer->head, xfer->max_blk_cnt);
edtla += xfer->data[i].bdone;
trb->dwTrb3 = (trb->dwTrb3 & ~0x1) | (hcb->ccs);
if (xfer->data[i].type == USB_DATA_PART) {
rem_len += xfer->data[i].blen;
i = index_inc(i, USB_MAX_XFER_BLOCKS);
i = index_inc(i, xfer->max_blk_cnt);
/* This 'continue' will delay the IOC behavior which
* could decrease the number of virtual interrupts.
@ -2769,7 +2816,7 @@ pci_xhci_xfer_complete(struct pci_xhci_vdev *xdev, struct usb_xfer *xfer,
!((err == XHCI_TRB_ERROR_SHORT_PKT) &&
(trb->dwTrb3 & XHCI_TRB_3_ISP_BIT))) {
i = index_inc(i, USB_MAX_XFER_BLOCKS);
i = index_inc(i, xfer->max_blk_cnt);
continue;
}
@ -2794,7 +2841,7 @@ pci_xhci_xfer_complete(struct pci_xhci_vdev *xdev, struct usb_xfer *xfer,
if (err != XHCI_TRB_ERROR_SUCCESS)
break;
i = index_inc(i, USB_MAX_XFER_BLOCKS);
i = index_inc(i, xfer->max_blk_cnt);
rem_len = 0;
}

View File

@ -336,7 +336,7 @@ umouse_request(void *scarg, struct usb_xfer *xfer)
}
xfer->data[idx].stat = USB_BLOCK_HANDLED;
idx = index_inc(idx, USB_MAX_XFER_BLOCKS);
idx = index_inc(idx, xfer->max_blk_cnt);
}
err = USB_ERR_NORMAL_COMPLETION;
@ -716,7 +716,7 @@ umouse_data_handler(void *scarg, struct usb_xfer *xfer, int dir,
data->stat = USB_BLOCK_HANDLED;
data = NULL;
idx = index_inc(idx, USB_MAX_XFER_BLOCKS);
idx = index_inc(idx, xfer->max_blk_cnt);
}
if (!data)
goto done;

View File

@ -18,7 +18,6 @@
#define LOG_TAG "USBPM: "
static struct usb_dev_sys_ctx_info g_ctx;
static uint8_t usb_dev_get_ep_type(struct usb_dev *udev, int pid, int epnum);
static uint16_t usb_dev_get_ep_maxp(struct usb_dev *udev, int pid, int epnum);
static bool
@ -246,8 +245,8 @@ usb_dev_comp_cb(struct libusb_transfer *trn)
idx = r->blk_head;
buf_idx = 0;
done = trn->actual_length;
while (index_valid(r->blk_head, r->blk_tail, USB_MAX_XFER_BLOCKS, idx))
{
while (index_valid(r->blk_head, r->blk_tail, xfer->max_blk_cnt, idx)) {
if (trn->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
buf_idx = 0;
buf = libusb_get_iso_packet_buffer_simple(trn, i);
@ -281,7 +280,7 @@ usb_dev_comp_cb(struct libusb_transfer *trn)
block->blen -= d;
block->bdone = d;
block->stat = USB_BLOCK_HANDLED;
idx = index_inc(idx, USB_MAX_XFER_BLOCKS);
idx = index_inc(idx, xfer->max_blk_cnt);
} while (block->type == USB_DATA_PART);
}
@ -290,10 +289,10 @@ stall_out:
if (is_stalled) {
idx = r->blk_head;
while (index_valid(r->blk_head, r->blk_tail,
USB_MAX_XFER_BLOCKS, idx)) {
xfer->max_blk_cnt, idx)) {
block = &xfer->data[idx];
block->stat = USB_BLOCK_HANDLED;
idx = index_inc(idx, USB_MAX_XFER_BLOCKS);
idx = index_inc(idx, xfer->max_blk_cnt);
}
}
@ -313,7 +312,7 @@ cancel_out:
if (r && r->buffer)
free(r->buffer);
xfer->requests[r->blk_head] = NULL;
xfer->reqs[r->blk_head] = NULL;
free(r);
free_transfer:
libusb_free_transfer(trn);
@ -368,11 +367,11 @@ usb_dev_prepare_xfer(struct usb_xfer *xfer, int *head, int *tail)
idx = xfer->head;
first = -1;
size = 0;
if (idx < 0 || idx >= USB_MAX_XFER_BLOCKS)
if (idx < 0 || idx >= xfer->max_blk_cnt)
return -1;
for (i = 0; i < xfer->ndata;
i++, idx = index_inc(idx, USB_MAX_XFER_BLOCKS)) {
i++, idx = index_inc(idx, xfer->max_blk_cnt)) {
block = &xfer->data[idx];
if (block->stat == USB_BLOCK_HANDLED ||
block->stat == USB_BLOCK_HANDLING)
@ -701,7 +700,7 @@ usb_dev_prepare_ctrl_xfer(struct usb_xfer *xfer)
idx = xfer->head;
if (idx < 0 || idx >= USB_MAX_XFER_BLOCKS)
if (idx < 0 || idx >= xfer->max_blk_cnt)
return NULL;
for (i = 0; i < xfer->ndata; i++) {
@ -714,7 +713,7 @@ usb_dev_prepare_ctrl_xfer(struct usb_xfer *xfer)
ret = blk;
blk->stat = USB_BLOCK_HANDLED;
idx = index_inc(idx, USB_MAX_XFER_BLOCKS);
idx = index_inc(idx, xfer->max_blk_cnt);
}
return ret;
}
@ -776,8 +775,8 @@ usb_dev_data(void *pdata, struct usb_xfer *xfer, int dir, int epctx)
UPRINTF(LDBG, "iso maxp %u framelen %d\r\n", maxp, framelen);
for (idx = head;
index_valid(head, tail, USB_MAX_XFER_BLOCKS, idx);
idx = index_inc(idx, USB_MAX_XFER_BLOCKS)) {
index_valid(head, tail, xfer->max_blk_cnt, idx);
idx = index_inc(idx, xfer->max_blk_cnt)) {
if (xfer->data[idx].blen > framelen)
UPRINTF(LFTL, "err framelen %d\r\n", framelen);
@ -804,7 +803,7 @@ usb_dev_data(void *pdata, struct usb_xfer *xfer, int dir, int epctx)
r->buf_size = size;
r->blk_head = head;
r->blk_tail = tail;
xfer->requests[head] = r;
xfer->reqs[head] = r;
UPRINTF(LDBG, "%s: %d-%s: explen %d ep%d-xfr [%d-%d %d] rq-%d "
"[%d-%d %d] dir %s type %s\r\n", __func__,
info->path.bus, usb_dev_path(&info->path), size, epctx,
@ -814,8 +813,8 @@ usb_dev_data(void *pdata, struct usb_xfer *xfer, int dir, int epctx)
if (!dir) {
for (idx = head, buf_idx = 0;
index_valid(head, tail, USB_MAX_XFER_BLOCKS, idx);
idx = index_inc(idx, USB_MAX_XFER_BLOCKS)) {
index_valid(head, tail, xfer->max_blk_cnt, idx);
idx = index_inc(idx, xfer->max_blk_cnt)) {
b = &xfer->data[idx];
if (b->type == USB_DATA_PART ||
b->type == USB_DATA_FULL) {
@ -827,8 +826,8 @@ usb_dev_data(void *pdata, struct usb_xfer *xfer, int dir, int epctx)
if (type == USB_ENDPOINT_ISOC) {
for (i = 0, idx = head;
index_valid(head, tail, USB_MAX_XFER_BLOCKS, idx);
idx = index_inc(idx, USB_MAX_XFER_BLOCKS)) {
index_valid(head, tail, xfer->max_blk_cnt, idx);
idx = index_inc(idx, xfer->max_blk_cnt)) {
int len = xfer->data[idx].blen;
if (xfer->data[idx].type == USB_DATA_NONE) {

View File

@ -106,7 +106,7 @@ usb_block_append(struct usb_xfer *xfer, void *buf, int blen, void *hcb,
{
struct usb_block *xb;
if (xfer->ndata >= USB_MAX_XFER_BLOCKS)
if (xfer->ndata >= xfer->max_blk_cnt)
return NULL;
if (hcb == NULL)
@ -120,7 +120,7 @@ usb_block_append(struct usb_xfer *xfer, void *buf, int blen, void *hcb,
xb->bdone = 0;
xb->type = USB_DATA_NONE;
xfer->ndata++;
xfer->tail = index_inc(xfer->tail, USB_MAX_XFER_BLOCKS);
xfer->tail = index_inc(xfer->tail, xfer->max_blk_cnt);
return xb;
}

View File

@ -42,8 +42,6 @@
* By default, the native xhci driver use two segments which contain 2 * 256
* trbs, so 1024 is enough currently.
*/
#define USB_MAX_XFER_BLOCKS 1024
#define USB_XFER_OUT 0
#define USB_XFER_IN 1
@ -164,9 +162,8 @@ struct usb_block {
};
struct usb_xfer {
uint64_t magic;
struct usb_block data[USB_MAX_XFER_BLOCKS];
struct usb_dev_req *requests[USB_MAX_XFER_BLOCKS];
struct usb_block *data;
struct usb_dev_req **reqs;
struct usb_device_request *ureq; /* setup ctl request */
int ndata; /* # of data items */
int head;
@ -174,6 +171,7 @@ struct usb_xfer {
void *dev; /* struct pci_xhci_dev_emu *dev */
int epid; /* related endpoint id */
int pid; /* token id */
int max_blk_cnt;
int status;
};

View File

@ -190,6 +190,15 @@ struct xhci_endp_ctx {
volatile uint32_t dwEpCtx7;
};
#define XHCI_EPTYPE_INVALID 0
#define XHCI_EPTYPE_ISOC_OUT 1
#define XHCI_EPTYPE_BULK_OUT 2
#define XHCI_EPTYPE_INT_OUT 3
#define XHCI_EPTYPE_CTRL 4
#define XHCI_EPTYPE_ISOC_IN 5
#define XHCI_EPTYPE_BULK_IN 6
#define XHCI_EPTYPE_INT_IN 7
struct xhci_input_ctx {
#define XHCI_INCTX_NON_CTRL_MASK 0xFFFFFFFCU
volatile uint32_t dwInCtx0;