Merge "msm: mhi_dev: Redesign UCI transfer request structure management"

This commit is contained in:
qctecmdr 2021-02-10 12:32:17 -08:00 • committed by Gerrit - the friendly Code Review server
commit bd060db274
3 changed files with 211 additions and 101 deletions

View file

@ -2137,25 +2137,34 @@ static void mhi_dev_transfer_completion_cb(void *mreq)
{
int rc = 0;
struct mhi_req *req = mreq;
struct mhi_dev_channel *ch = req->client->channel;
struct mhi_dev_channel *ch;
u32 snd_cmpl = req->snd_cmpl;
bool inbound = false;
if (mhi_ctx->ch_ctx_cache[ch->ch_id].ch_type ==
MHI_DEV_CH_TYPE_INBOUND_CHANNEL)
ch->pend_wr_count--;
ch = &mhi_ctx->ch[req->chan];
dma_unmap_single(&mhi_ctx->pdev->dev, req->dma,
req->len, DMA_FROM_DEVICE);
req->len, DMA_FROM_DEVICE);
if (mhi_ctx->ch_ctx_cache[ch->ch_id].ch_type ==
MHI_DEV_CH_TYPE_INBOUND_CHANNEL) {
inbound = true;
ch->pend_wr_count--;
}
/*
* Channel got stopped or closed with transfers pending
* Channel got closed with transfers pending
* Do not trigger callback or send cmpl to host
*/
if (ch->state == MHI_DEV_CH_CLOSED ||
ch->state == MHI_DEV_CH_STOPPED) {
mhi_log(MHI_MSG_DBG,
"Ch %d not in started state, %d writes pending\n",
if (inbound)
mhi_log(MHI_MSG_DBG,
"Ch %d closed with %d writes pending\n",
ch->ch_id, ch->pend_wr_count + 1);
else
mhi_log(MHI_MSG_DBG,
"Ch %d closed with read pending\n", ch->ch_id);
return;
}
@ -2707,6 +2716,7 @@ int mhi_dev_open_channel(uint32_t chan_id,
ch->active_client = (*handle_client);
(*handle_client)->channel = ch;
(*handle_client)->event_trigger = mhi_dev_client_cb_reason;
ch->pend_wr_count = 0;
if (ch->state == MHI_DEV_CH_UNINT) {
ch->ring = &mhi_ctx->ring[chan_id + mhi_ctx->ch_ring_start];

View file

@ -24,8 +24,8 @@
#define MHI_SOFTWARE_CLIENT_LIMIT (MHI_MAX_SOFTWARE_CHANNELS/2)
#define MHI_UCI_IPC_LOG_PAGES (100)
/* Max number of MHI write request structures (used in async writes) */
#define MHI_UCI_NUM_WR_REQ_DEFAULT 10
/* Max number of MHI read/write request structs (used in async transfers) */
#define MHI_UCI_NUM_REQ_DEFAULT 10
#define MAX_NR_TRBS_PER_CHAN 9
#define MHI_QTI_IFACE_ID 4
#define MHI_ADPL_IFACE_ID 5
@ -42,6 +42,8 @@
#define MHI_UCI_RELEASE_TIMEOUT_MAX 5100
#define MHI_UCI_RELEASE_TIMEOUT_COUNT 30
#define MHI_UCI_IS_CHAN_DIR_IN(n) ((n % 2) ? true : false)
enum uci_dbg_level {
UCI_DBG_VERBOSE = 0x0,
UCI_DBG_INFO = 0x1,
@ -86,8 +88,7 @@ struct chan_attr {
/* Skip node creation if not needed */
bool skip_node;
/* Number of write request structs to allocate */
u32 num_wr_reqs;
u32 num_reqs;
};
static void mhi_uci_generic_client_cb(struct mhi_dev_client_cb_data *cb_data);
@ -348,15 +349,19 @@ struct uci_client {
struct mhi_uci_ctxt_t *uci_ctxt;
struct mutex in_chan_lock;
struct mutex out_chan_lock;
spinlock_t wr_req_lock;
spinlock_t req_lock;
unsigned int f_flags;
struct mhi_req *wreqs;
struct list_head wr_req_list;
/* Pointer to dynamically allocated mhi_req structs */
struct mhi_req *reqs;
/* Pointer to available (free) reqs */
struct list_head req_list;
/* Pointer to in-use reqs */
struct list_head in_use_list;
struct completion read_done;
struct completion at_ctrl_read_done;
struct completion *write_done;
int (*send)(struct uci_client *h, void *data_loc, u32 size);
int (*read)(struct uci_client *h, struct mhi_req *ureq, int *bytes);
int (*read)(struct uci_client *h, int *bytes);
unsigned int tiocm;
unsigned int at_ctrl_mask;
};
@ -490,23 +495,75 @@ free_memory:
return rc;
}
static struct mhi_req *mhi_uci_get_req(struct uci_client *uci_handle)
{
struct mhi_req *req;
unsigned long flags;
spin_lock_irqsave(&uci_handle->req_lock, flags);
if (list_empty(&uci_handle->req_list)) {
uci_log(UCI_DBG_ERROR, "Request pool empty for chans %d, %d\n",
uci_handle->in_chan, uci_handle->out_chan);
spin_unlock_irqrestore(&uci_handle->req_lock, flags);
return NULL;
}
/* Remove from free list and add to in-use list */
req = container_of(uci_handle->req_list.next,
struct mhi_req, list);
list_del_init(&req->list);
/*
* If req is marked stale and if it was used for the write channel
* to host, free the previously allocated input buffer before the
* req is re-used
*/
if (req->is_stale && req->buf && MHI_UCI_IS_CHAN_DIR_IN(req->chan)) {
uci_log(UCI_DBG_VERBOSE, "Freeing write buf for chan %d\n",
req->chan);
kfree(req->buf);
}
req->is_stale = false;
uci_log(UCI_DBG_VERBOSE, "Adding req to in-use list\n");
list_add_tail(&req->list, &uci_handle->in_use_list);
spin_unlock_irqrestore(&uci_handle->req_lock, flags);
return req;
}
static void mhi_uci_put_req(struct uci_client *uci_handle, struct mhi_req *req)
{
unsigned long flags;
spin_lock_irqsave(&uci_handle->req_lock, flags);
/* Remove from in-use list and add back to free list */
list_del_init(&req->list);
list_add_tail(&req->list, &uci_handle->req_list);
spin_unlock_irqrestore(&uci_handle->req_lock, flags);
}
static void mhi_uci_write_completion_cb(void *req)
{
struct mhi_req *ureq = req;
struct uci_client *uci_handle;
unsigned long flags;
struct uci_client *uci_handle = (struct uci_client *)ureq->context;
uci_handle = (struct uci_client *)ureq->context;
kfree(ureq->buf);
ureq->buf = NULL;
spin_lock_irqsave(&uci_handle->wr_req_lock, flags);
list_add_tail(&ureq->list, &uci_handle->wr_req_list);
spin_unlock_irqrestore(&uci_handle->wr_req_lock, flags);
/*
* If this is a delayed write completion, just clear
* the stale flag and return. The ureq was added to
* the free list when client called release function.
*/
if (ureq->is_stale) {
uci_log(UCI_DBG_VERBOSE,
"Got stale completion for ch %d\n", ureq->chan);
ureq->is_stale = false;
return;
}
if (uci_handle->write_done)
complete(uci_handle->write_done);
mhi_uci_put_req(uci_handle, ureq);
/* Write queue may be waiting for write request structs */
wake_up(&uci_handle->write_wq);
}
@ -516,7 +573,19 @@ static void mhi_uci_read_completion_cb(void *req)
struct mhi_req *ureq = req;
struct uci_client *uci_handle;
if (ureq->is_stale) {
uci_log(UCI_DBG_VERBOSE,
"Got stale completion for ch %d, ignoring\n",
ureq->chan);
return;
}
uci_handle = (struct uci_client *)ureq->context;
uci_handle->pkt_loc = (void *)ureq->buf;
uci_handle->pkt_size = ureq->transfer_len;
mhi_uci_put_req(uci_handle, ureq);
complete(&uci_handle->read_done);
}
@ -526,6 +595,9 @@ static int mhi_uci_send_sync(struct uci_client *uci_handle,
struct mhi_req ureq;
int ret_val;
uci_log(UCI_DBG_VERBOSE,
"Sync write for ch %d size %d\n", uci_handle->out_chan, size);
ureq.client = uci_handle->out_handle;
ureq.buf = data_loc;
ureq.len = size;
@ -546,19 +618,12 @@ static int mhi_uci_send_async(struct uci_client *uci_handle,
struct mhi_req *ureq;
uci_log(UCI_DBG_VERBOSE,
"Got async write for ch %d of size %d\n",
"Async write for ch %d size %d\n",
uci_handle->out_chan, size);
spin_lock_irq(&uci_handle->wr_req_lock);
if (list_empty(&uci_handle->wr_req_list)) {
uci_log(UCI_DBG_ERROR, "Write request pool empty\n");
spin_unlock_irq(&uci_handle->wr_req_lock);
ureq = mhi_uci_get_req(uci_handle);
if (!ureq)
return -EBUSY;
}
ureq = container_of(uci_handle->wr_req_list.next,
struct mhi_req, list);
list_del_init(&ureq->list);
spin_unlock_irq(&uci_handle->wr_req_lock);
ureq->client = uci_handle->out_handle;
ureq->context = uci_handle;
@ -577,9 +642,7 @@ static int mhi_uci_send_async(struct uci_client *uci_handle,
error_async_transfer:
ureq->buf = NULL;
spin_lock_irq(&uci_handle->wr_req_lock);
list_add_tail(&ureq->list, &uci_handle->wr_req_list);
spin_unlock_irq(&uci_handle->wr_req_lock);
mhi_uci_put_req(uci_handle, ureq);
return bytes_to_write;
}
@ -621,7 +684,7 @@ static int mhi_uci_send_packet(struct uci_client *uci_handle, void *data_loc,
return -EAGAIN;
ret_val = wait_event_interruptible_timeout(
uci_handle->write_wq,
!list_empty(&uci_handle->wr_req_list),
!list_empty(&uci_handle->req_list),
MHI_UCI_WRITE_REQ_AVAIL_TIMEOUT);
if (ret_val > 0) {
/*
@ -722,42 +785,62 @@ static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait)
return mask;
}
static int mhi_uci_alloc_write_reqs(struct uci_client *client)
static int mhi_uci_alloc_reqs(struct uci_client *client)
{
int i;
u32 num_wr_reqs;
u32 num_reqs;
num_wr_reqs = client->in_chan_attr->num_wr_reqs;
if (!num_wr_reqs)
num_wr_reqs = MHI_UCI_NUM_WR_REQ_DEFAULT;
if (client->reqs) {
uci_log(UCI_DBG_VERBOSE, "Reqs already allocated\n");
return 0;
}
client->wreqs = kcalloc(num_wr_reqs,
num_reqs = client->in_chan_attr->num_reqs;
if (!num_reqs)
num_reqs = MHI_UCI_NUM_REQ_DEFAULT;
client->reqs = kcalloc(num_reqs,
sizeof(struct mhi_req),
GFP_KERNEL);
if (!client->wreqs) {
uci_log(UCI_DBG_ERROR, "Write reqs alloc failed\n");
if (!client->reqs) {
uci_log(UCI_DBG_ERROR, "Reqs alloc failed\n");
return -ENOMEM;
}
INIT_LIST_HEAD(&client->wr_req_list);
for (i = 0; i < num_wr_reqs; ++i)
list_add_tail(&client->wreqs[i].list, &client->wr_req_list);
INIT_LIST_HEAD(&client->req_list);
INIT_LIST_HEAD(&client->in_use_list);
for (i = 0; i < num_reqs; ++i)
list_add_tail(&client->reqs[i].list, &client->req_list);
uci_log(UCI_DBG_INFO,
"Allocated %d write reqs for chan %d\n",
num_wr_reqs, client->out_chan);
num_reqs, client->out_chan);
return 0;
}
static int mhi_uci_read_async(struct uci_client *uci_handle,
struct mhi_req *ureq, int *bytes_avail)
static int mhi_uci_read_async(struct uci_client *uci_handle, int *bytes_avail)
{
int ret_val = 0;
unsigned long compl_ret;
struct mhi_req *ureq;
struct mhi_dev_client *client_handle;
uci_log(UCI_DBG_ERROR,
"Async read for ch %d\n", uci_handle->in_chan);
ureq = mhi_uci_get_req(uci_handle);
if (!ureq) {
uci_log(UCI_DBG_ERROR,
"Out of reqs for chan %d\n", uci_handle->in_chan);
return -EBUSY;
}
client_handle = uci_handle->in_handle;
ureq->chan = uci_handle->in_chan;
ureq->client = client_handle;
ureq->buf = uci_handle->in_buf_list[0].addr;
ureq->len = uci_handle->in_buf_list[0].buf_size;
ureq->mode = DMA_ASYNC;
ureq->client_cb = mhi_uci_read_completion_cb;
ureq->snd_cmpl = 1;
@ -766,14 +849,14 @@ static int mhi_uci_read_async(struct uci_client *uci_handle,
reinit_completion(&uci_handle->read_done);
*bytes_avail = mhi_dev_read_channel(ureq);
uci_log(UCI_DBG_VERBOSE, "buf_size = 0x%lx bytes_read = 0x%x\n",
ureq->len, *bytes_avail);
if (*bytes_avail < 0) {
uci_log(UCI_DBG_ERROR, "Failed to read channel ret %dlu\n",
*bytes_avail);
mhi_uci_put_req(uci_handle, ureq);
return -EIO;
}
uci_log(UCI_DBG_VERBOSE, "buf_size = 0x%lx bytes_read = 0x%x\n",
ureq->len, *bytes_avail);
if (*bytes_avail > 0) {
uci_log(UCI_DBG_VERBOSE,
"Waiting for async read completion!\n");
@ -781,7 +864,6 @@ static int mhi_uci_read_async(struct uci_client *uci_handle,
wait_for_completion_interruptible_timeout(
&uci_handle->read_done,
MHI_UCI_ASYNC_READ_TIMEOUT);
if (compl_ret == -ERESTARTSYS) {
uci_log(UCI_DBG_ERROR, "Exit signal caught\n");
return compl_ret;
@ -791,33 +873,41 @@ static int mhi_uci_read_async(struct uci_client *uci_handle,
return -EIO;
}
uci_log(UCI_DBG_VERBOSE,
"wk up Read completed on ch %d\n", ureq->chan);
uci_handle->pkt_loc = (void *)ureq->buf;
uci_handle->pkt_size = ureq->transfer_len;
"wk up Read completed on ch %d\n", uci_handle->in_chan);
uci_log(UCI_DBG_VERBOSE,
"Got pkt of sz 0x%lx at adr %pK, ch %d\n",
uci_handle->pkt_size,
ureq->buf, ureq->chan);
uci_handle->pkt_loc, uci_handle->in_chan);
} else {
uci_handle->pkt_loc = NULL;
uci_handle->pkt_size = 0;
uci_log(UCI_DBG_VERBOSE,
"No read data available, return req to free liat\n");
mhi_uci_put_req(uci_handle, ureq);
}
return ret_val;
}
static int mhi_uci_read_sync(struct uci_client *uci_handle,
struct mhi_req *ureq, int *bytes_avail)
static int mhi_uci_read_sync(struct uci_client *uci_handle, int *bytes_avail)
{
int ret_val = 0;
struct mhi_req ureq;
struct mhi_dev_client *client_handle;
ureq->mode = DMA_SYNC;
*bytes_avail = mhi_dev_read_channel(ureq);
uci_log(UCI_DBG_ERROR,
"Sync read for ch %d\n", uci_handle->in_chan);
client_handle = uci_handle->in_handle;
ureq.chan = uci_handle->in_chan;
ureq.client = client_handle;
ureq.buf = uci_handle->in_buf_list[0].addr;
ureq.len = uci_handle->in_buf_list[0].buf_size;
ureq.mode = DMA_SYNC;
*bytes_avail = mhi_dev_read_channel(&ureq);
uci_log(UCI_DBG_VERBOSE, "buf_size = 0x%lx bytes_read = 0x%x\n",
ureq->len, *bytes_avail);
ureq.len, *bytes_avail);
if (*bytes_avail < 0) {
uci_log(UCI_DBG_ERROR, "Failed to read channel ret %d\n",
@ -826,13 +916,13 @@ static int mhi_uci_read_sync(struct uci_client *uci_handle,
}
if (*bytes_avail > 0) {
uci_handle->pkt_loc = (void *)ureq->buf;
uci_handle->pkt_size = ureq->transfer_len;
uci_handle->pkt_loc = (void *)ureq.buf;
uci_handle->pkt_size = ureq.transfer_len;
uci_log(UCI_DBG_VERBOSE,
"Got pkt of sz 0x%lx at adr %pK, ch %d\n",
uci_handle->pkt_size,
ureq->buf, ureq->chan);
ureq.buf, ureq.chan);
} else {
uci_handle->pkt_loc = NULL;
uci_handle->pkt_size = 0;
@ -857,7 +947,7 @@ static int open_client_mhi_channels(struct uci_client *uci_client)
/* Allocate write requests for async operations */
if (!(uci_client->f_flags & O_SYNC)) {
rc = mhi_uci_alloc_write_reqs(uci_client);
rc = mhi_uci_alloc_reqs(uci_client);
if (rc)
goto handle_not_rdy_err;
uci_client->send = mhi_uci_send_async;
@ -978,6 +1068,7 @@ static int mhi_uci_client_release(struct inode *mhi_inode,
{
struct uci_client *uci_handle = file_handle->private_data;
int count = 0;
struct mhi_req *ureq;
if (!uci_handle)
return -EINVAL;
@ -1008,9 +1099,6 @@ static int mhi_uci_client_release(struct inode *mhi_inode,
if (atomic_read(&uci_handle->mhi_chans_open)) {
atomic_set(&uci_handle->mhi_chans_open, 0);
if (!(uci_handle->f_flags & O_SYNC))
kfree(uci_handle->wreqs);
mutex_lock(&uci_handle->out_chan_lock);
mhi_dev_close_channel(uci_handle->out_handle);
wake_up(&uci_handle->write_wq);
@ -1020,6 +1108,27 @@ static int mhi_uci_client_release(struct inode *mhi_inode,
mhi_dev_close_channel(uci_handle->in_handle);
wake_up(&uci_handle->read_wq);
mutex_unlock(&uci_handle->in_chan_lock);
/*
* Add back reqs for in-use list, if any, to free list.
* Mark the ureq stale to avoid returning stale data
* to client if the transfer completes later.
*/
count = 0;
while (!(list_empty(&uci_handle->in_use_list))) {
ureq = container_of(uci_handle->in_use_list.next,
struct mhi_req, list);
list_del_init(&ureq->list);
ureq->is_stale = true;
uci_log(UCI_DBG_VERBOSE,
"Adding back req for chan %d to free list\n",
ureq->chan);
list_add_tail(&ureq->list, &uci_handle->req_list);
count++;
}
if (count)
uci_log(UCI_DBG_DBG,
"Client %d closed with %d transfers pending\n",
iminor(mhi_inode), count);
}
atomic_set(&uci_handle->read_data_ready, 0);
@ -1136,20 +1245,11 @@ static int __mhi_uci_client_read(struct uci_client *uci_handle,
int *bytes_avail)
{
int ret_val = 0;
struct mhi_dev_client *client_handle;
struct mhi_req ureq;
client_handle = uci_handle->in_handle;
ureq.chan = uci_handle->in_chan;
ureq.client = client_handle;
ureq.buf = uci_handle->in_buf_list[0].addr;
ureq.len = uci_handle->in_buf_list[0].buf_size;
do {
if (!uci_handle->pkt_loc &&
!atomic_read(&uci_ctxt.mhi_disabled)) {
ret_val = uci_handle->read(uci_handle, &ureq,
bytes_avail);
ret_val = uci_handle->read(uci_handle, bytes_avail);
if (ret_val)
return ret_val;
}
@ -1159,12 +1259,13 @@ static int __mhi_uci_client_read(struct uci_client *uci_handle,
uci_log(UCI_DBG_VERBOSE,
"No data read_data_ready %d, chan %d\n",
atomic_read(&uci_handle->read_data_ready),
ureq.chan);
uci_handle->in_chan);
if (uci_handle->f_flags & (O_NONBLOCK | O_NDELAY))
return -EAGAIN;
ret_val = wait_event_interruptible(uci_handle->read_wq,
(!mhi_dev_channel_isempty(client_handle)));
(!mhi_dev_channel_isempty(
uci_handle->in_handle)));
if (ret_val == -ERESTARTSYS) {
uci_log(UCI_DBG_ERROR, "Exit signal caught\n");
@ -1173,26 +1274,16 @@ static int __mhi_uci_client_read(struct uci_client *uci_handle,
uci_log(UCI_DBG_VERBOSE,
"wk up Got data on ch %d read_data_ready %d\n",
ureq.chan,
uci_handle->in_chan,
atomic_read(&uci_handle->read_data_ready));
} else if (*bytes_avail > 0) {
/* A valid packet was returned from MHI */
uci_log(UCI_DBG_VERBOSE,
"Got packet: avail pkts %d phy_adr %pK, ch %d\n",
atomic_read(&uci_handle->read_data_ready),
ureq.buf,
ureq.chan);
} else {
/*
* MHI did not return a valid packet, but we have one
* which we did not finish returning to user
*/
uci_log(UCI_DBG_CRITICAL,
"chan %d err: avail pkts %d phy_adr %pK",
ureq.chan,
atomic_read(&uci_handle->read_data_ready),
ureq.buf);
return -EIO;
uci_handle->pkt_loc,
uci_handle->in_chan);
break;
}
} while (!uci_handle->pkt_loc);
@ -1454,7 +1545,7 @@ static int mhi_register_client(struct uci_client *mhi_client, int index)
mutex_init(&mhi_client->in_chan_lock);
mutex_init(&mhi_client->out_chan_lock);
spin_lock_init(&mhi_client->wr_req_lock);
spin_lock_init(&mhi_client->req_lock);
/* Init the completion event for AT ctrl read */
init_completion(&mhi_client->at_ctrl_read_done);
@ -1822,6 +1913,7 @@ static void mhi_uci_at_ctrl_client_cb(struct mhi_dev_client_cb_data *cb_data)
{
struct uci_client *client = cb_data->user_data;
int rc;
struct mhi_req *ureq;
uci_log(UCI_DBG_VERBOSE, " Rcvd MHI cb for channel %d, state %d\n",
cb_data->channel, cb_data->ctrl_info);
@ -1848,10 +1940,17 @@ static void mhi_uci_at_ctrl_client_cb(struct mhi_dev_client_cb_data *cb_data)
}
destroy_workqueue(uci_ctxt.at_ctrl_wq);
uci_ctxt.at_ctrl_wq = NULL;
if (!(client->f_flags & O_SYNC))
kfree(client->wreqs);
mhi_dev_close_channel(client->out_handle);
mhi_dev_close_channel(client->in_handle);
/* Add back reqs for in-use list, if any, to free list */
while (!(list_empty(&client->in_use_list))) {
ureq = container_of(client->in_use_list.next,
struct mhi_req, list);
list_del_init(&ureq->list);
/* Add to in-use list */
list_add_tail(&ureq->list, &client->req_list);
}
}
}

View file

@ -64,6 +64,7 @@ struct mhi_req {
struct list_head list;
union mhi_dev_ring_element_type *el;
void (*client_cb)(void *req);
bool is_stale;
};
/* SW channel client list */