/* Flush cache lines associated with iobuf list */ static void flush_iob_list(struct nvshm_handle *handle, struct nvshm_iobuf *iob) { struct nvshm_iobuf *phy_list, *leaf, *next, *sg_next; phy_list = iob; while (phy_list) { leaf = phy_list; next = phy_list->next; while (leaf) { sg_next = leaf->sg_next; WARN_ON_ONCE(nvshm_iobuf_check(leaf) < 0); /* Flush associated data */ if (leaf->length) { FLUSH_CPU_DCACHE(NVSHM_B2A(handle, (int)leaf->npduData + leaf->dataOffset), leaf->length); } /* Flush iobuf */ FLUSH_CPU_DCACHE(leaf, sizeof(struct nvshm_iobuf)); if (sg_next) leaf = NVSHM_B2A(handle, sg_next); else leaf = NULL; } if (next) phy_list = NVSHM_B2A(handle, next); else phy_list = NULL; } }
struct nvshm_iobuf *nvshm_iobuf_alloc(struct nvshm_channel *chan, int size) { struct nvshm_handle *handle = nvshm_get_handle(); struct nvshm_iobuf *desc = NULL; unsigned long f; spin_lock_irqsave(&alloc.lock, f); if (alloc.free_pool_head) { int check = nvshm_iobuf_check(alloc.free_pool_head); if (check) { spin_unlock_irqrestore(&alloc.lock, f); pr_err("%s: iobuf check ret %d\n", __func__, check); return NULL; } if (size > (alloc.free_pool_head->total_length - NVSHM_DEFAULT_OFFSET)) { spin_unlock_irqrestore(&alloc.lock, f); pr_err("%s: requested size (%d > %d) too big\n", __func__, size, alloc.free_pool_head->total_length - NVSHM_DEFAULT_OFFSET); if (chan->ops) { chan->ops->error_event(chan, NVSHM_IOBUF_ERROR); } return desc; } desc = alloc.free_pool_head; alloc.free_pool_head = desc->next; if (alloc.free_pool_head) { alloc.free_pool_head = NVSHM_B2A(handle, alloc.free_pool_head); } else { pr_debug("%s end of alloc queue - clearing tail\n", __func__); alloc.free_pool_tail = NULL; } desc->length = 0; desc->flags = 0; desc->data_offset = NVSHM_DEFAULT_OFFSET; desc->sg_next = NULL; desc->next = NULL; desc->ref = 1; } else { spin_unlock_irqrestore(&alloc.lock, f); pr_err("%s: no more alloc space\n", __func__); /* No error since it's only Xoff situation */ return desc; } spin_unlock_irqrestore(&alloc.lock, f); return desc; }
/* Return 0 if ok or non zero otherwise */ static int inv_iob_list(struct nvshm_handle *handle, struct nvshm_iobuf *iob) { struct nvshm_iobuf *phy_list, *leaf; phy_list = iob; while (phy_list) { leaf = phy_list; while (leaf) { /* Check leaf address before any operation on it */ /* Cannot use nvshm_iobuf_check because iobuf */ /* is not invalidated so content will be wrong */ if (ADDR_OUTSIDE(leaf, handle->ipc_base_virt, handle->ipc_size)) { return -EIO; } /* Invalidate iobuf */ INV_CPU_DCACHE(leaf, sizeof(struct nvshm_iobuf)); /* Check iobuf */ if (nvshm_iobuf_check(leaf)) return -EIO; /* Invalidate associated data */ if (leaf->length) { INV_CPU_DCACHE(NVSHM_B2A(handle, (int)leaf->npduData + leaf->dataOffset), leaf->length); } if (leaf->sg_next) leaf = NVSHM_B2A(handle, leaf->sg_next); else leaf = NULL; } if (phy_list->next) phy_list = NVSHM_B2A(handle, phy_list->next); else phy_list = NULL; } return 0; }