linux-stable/drivers/misc/mic/scif/scif_fence.c
Wenwen Wang 436a472176 misc: mic: fix a DMA pool free failure
[ Upstream commit 6b995f4eec ]

In _scif_prog_signal(), the boolean variable 'x100' is used to indicate
whether the MIC Coprocessor is X100. If 'x100' is true, the status
descriptor will be used to write the value to the destination. Otherwise, a
DMA pool will be allocated for this purpose. Specifically, if the DMA pool
is allocated successfully, two memory addresses will be returned. One is
for the CPU and the other is for the device to access the DMA pool. The
former is stored to the variable 'status' and the latter is stored to the
variable 'src'. After the allocation, the address in 'src' is saved to
'status->src_dma_addr', which is actually in the DMA pool, and 'src' is
then modified.

Later on, if an error occurs, the execution flow will transfer to the label
'dma_fail', which will check 'x100' and free up the allocated DMA pool if
'x100' is false. The point here is that 'status->src_dma_addr' is used for
freeing up the DMA pool. As mentioned before, 'status->src_dma_addr' is in
the DMA pool. And thus, the device is able to modify this data. This can
potentially cause failures when freeing up the DMA pool because of the
modified device address.

This patch avoids the above issue by using the variable 'src' (with
necessary calculation) to free up the DMA pool.

Signed-off-by: Wenwen Wang <wang6495@umn.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
2019-12-01 09:13:23 +01:00

772 lines
20 KiB
C

/*
* Intel MIC Platform Software Stack (MPSS)
*
* Copyright(c) 2015 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* Intel SCIF driver.
*
*/
#include "scif_main.h"
/**
* scif_recv_mark: Handle SCIF_MARK request
* @msg: Interrupt message
*
* The peer has requested a mark.
*/
void scif_recv_mark(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
int mark = 0;
int err;
err = _scif_fence_mark(ep, &mark);
if (err)
msg->uop = SCIF_MARK_NACK;
else
msg->uop = SCIF_MARK_ACK;
msg->payload[0] = ep->remote_ep;
msg->payload[2] = mark;
scif_nodeqp_send(ep->remote_dev, msg);
}
/**
* scif_recv_mark_resp: Handle SCIF_MARK_(N)ACK messages.
* @msg: Interrupt message
*
* The peer has responded to a SCIF_MARK message.
*/
void scif_recv_mark_resp(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
struct scif_fence_info *fence_req =
(struct scif_fence_info *)msg->payload[1];
mutex_lock(&ep->rma_info.rma_lock);
if (msg->uop == SCIF_MARK_ACK) {
fence_req->state = OP_COMPLETED;
fence_req->dma_mark = (int)msg->payload[2];
} else {
fence_req->state = OP_FAILED;
}
mutex_unlock(&ep->rma_info.rma_lock);
complete(&fence_req->comp);
}
/**
* scif_recv_wait: Handle SCIF_WAIT request
* @msg: Interrupt message
*
* The peer has requested waiting on a fence.
*/
void scif_recv_wait(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
struct scif_remote_fence_info *fence;
/*
* Allocate structure for remote fence information and
* send a NACK if the allocation failed. The peer will
* return ENOMEM upon receiving a NACK.
*/
fence = kmalloc(sizeof(*fence), GFP_KERNEL);
if (!fence) {
msg->payload[0] = ep->remote_ep;
msg->uop = SCIF_WAIT_NACK;
scif_nodeqp_send(ep->remote_dev, msg);
return;
}
/* Prepare the fence request */
memcpy(&fence->msg, msg, sizeof(struct scifmsg));
INIT_LIST_HEAD(&fence->list);
/* Insert to the global remote fence request list */
mutex_lock(&scif_info.fencelock);
atomic_inc(&ep->rma_info.fence_refcount);
list_add_tail(&fence->list, &scif_info.fence);
mutex_unlock(&scif_info.fencelock);
schedule_work(&scif_info.misc_work);
}
/**
* scif_recv_wait_resp: Handle SCIF_WAIT_(N)ACK messages.
* @msg: Interrupt message
*
* The peer has responded to a SCIF_WAIT message.
*/
void scif_recv_wait_resp(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
struct scif_fence_info *fence_req =
(struct scif_fence_info *)msg->payload[1];
mutex_lock(&ep->rma_info.rma_lock);
if (msg->uop == SCIF_WAIT_ACK)
fence_req->state = OP_COMPLETED;
else
fence_req->state = OP_FAILED;
mutex_unlock(&ep->rma_info.rma_lock);
complete(&fence_req->comp);
}
/**
* scif_recv_sig_local: Handle SCIF_SIG_LOCAL request
* @msg: Interrupt message
*
* The peer has requested a signal on a local offset.
*/
void scif_recv_sig_local(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
int err;
err = scif_prog_signal(ep, msg->payload[1], msg->payload[2],
SCIF_WINDOW_SELF);
if (err)
msg->uop = SCIF_SIG_NACK;
else
msg->uop = SCIF_SIG_ACK;
msg->payload[0] = ep->remote_ep;
scif_nodeqp_send(ep->remote_dev, msg);
}
/**
* scif_recv_sig_remote: Handle SCIF_SIGNAL_REMOTE request
* @msg: Interrupt message
*
* The peer has requested a signal on a remote offset.
*/
void scif_recv_sig_remote(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
int err;
err = scif_prog_signal(ep, msg->payload[1], msg->payload[2],
SCIF_WINDOW_PEER);
if (err)
msg->uop = SCIF_SIG_NACK;
else
msg->uop = SCIF_SIG_ACK;
msg->payload[0] = ep->remote_ep;
scif_nodeqp_send(ep->remote_dev, msg);
}
/**
* scif_recv_sig_resp: Handle SCIF_SIG_(N)ACK messages.
* @msg: Interrupt message
*
* The peer has responded to a signal request.
*/
void scif_recv_sig_resp(struct scif_dev *scifdev, struct scifmsg *msg)
{
struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0];
struct scif_fence_info *fence_req =
(struct scif_fence_info *)msg->payload[3];
mutex_lock(&ep->rma_info.rma_lock);
if (msg->uop == SCIF_SIG_ACK)
fence_req->state = OP_COMPLETED;
else
fence_req->state = OP_FAILED;
mutex_unlock(&ep->rma_info.rma_lock);
complete(&fence_req->comp);
}
static inline void *scif_get_local_va(off_t off, struct scif_window *window)
{
struct page **pages = window->pinned_pages->pages;
int page_nr = (off - window->offset) >> PAGE_SHIFT;
off_t page_off = off & ~PAGE_MASK;
return page_address(pages[page_nr]) + page_off;
}
static void scif_prog_signal_cb(void *arg)
{
struct scif_status *status = arg;
dma_pool_free(status->ep->remote_dev->signal_pool, status,
status->src_dma_addr);
}
static int _scif_prog_signal(scif_epd_t epd, dma_addr_t dst, u64 val)
{
struct scif_endpt *ep = (struct scif_endpt *)epd;
struct dma_chan *chan = ep->rma_info.dma_chan;
struct dma_device *ddev = chan->device;
bool x100 = !is_dma_copy_aligned(chan->device, 1, 1, 1);
struct dma_async_tx_descriptor *tx;
struct scif_status *status = NULL;
dma_addr_t src;
dma_cookie_t cookie;
int err;
tx = ddev->device_prep_dma_memcpy(chan, 0, 0, 0, DMA_PREP_FENCE);
if (!tx) {
err = -ENOMEM;
dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n",
__func__, __LINE__, err);
goto alloc_fail;
}
cookie = tx->tx_submit(tx);
if (dma_submit_error(cookie)) {
err = (int)cookie;
dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n",
__func__, __LINE__, err);
goto alloc_fail;
}
dma_async_issue_pending(chan);
if (x100) {
/*
* For X100 use the status descriptor to write the value to
* the destination.
*/
tx = ddev->device_prep_dma_imm_data(chan, dst, val, 0);
} else {
status = dma_pool_alloc(ep->remote_dev->signal_pool, GFP_KERNEL,
&src);
if (!status) {
err = -ENOMEM;
dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n",
__func__, __LINE__, err);
goto alloc_fail;
}
status->val = val;
status->src_dma_addr = src;
status->ep = ep;
src += offsetof(struct scif_status, val);
tx = ddev->device_prep_dma_memcpy(chan, dst, src, sizeof(val),
DMA_PREP_INTERRUPT);
}
if (!tx) {
err = -ENOMEM;
dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n",
__func__, __LINE__, err);
goto dma_fail;
}
if (!x100) {
tx->callback = scif_prog_signal_cb;
tx->callback_param = status;
}
cookie = tx->tx_submit(tx);
if (dma_submit_error(cookie)) {
err = -EIO;
dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n",
__func__, __LINE__, err);
goto dma_fail;
}
dma_async_issue_pending(chan);
return 0;
dma_fail:
if (!x100)
dma_pool_free(ep->remote_dev->signal_pool, status,
src - offsetof(struct scif_status, val));
alloc_fail:
return err;
}
/*
* scif_prog_signal:
* @epd - Endpoint Descriptor
* @offset - registered address to write @val to
* @val - Value to be written at @offset
* @type - Type of the window.
*
* Arrange to write a value to the registered offset after ensuring that the
* offset provided is indeed valid.
*/
int scif_prog_signal(scif_epd_t epd, off_t offset, u64 val,
enum scif_window_type type)
{
struct scif_endpt *ep = (struct scif_endpt *)epd;
struct scif_window *window = NULL;
struct scif_rma_req req;
dma_addr_t dst_dma_addr;
int err;
mutex_lock(&ep->rma_info.rma_lock);
req.out_window = &window;
req.offset = offset;
req.nr_bytes = sizeof(u64);
req.prot = SCIF_PROT_WRITE;
req.type = SCIF_WINDOW_SINGLE;
if (type == SCIF_WINDOW_SELF)
req.head = &ep->rma_info.reg_list;
else
req.head = &ep->rma_info.remote_reg_list;
/* Does a valid window exist? */
err = scif_query_window(&req);
if (err) {
dev_err(scif_info.mdev.this_device,
"%s %d err %d\n", __func__, __LINE__, err);
goto unlock_ret;
}
if (scif_is_mgmt_node() && scifdev_self(ep->remote_dev)) {
u64 *dst_virt;
if (type == SCIF_WINDOW_SELF)
dst_virt = scif_get_local_va(offset, window);
else
dst_virt =
scif_get_local_va(offset, (struct scif_window *)
window->peer_window);
*dst_virt = val;
} else {
dst_dma_addr = __scif_off_to_dma_addr(window, offset);
err = _scif_prog_signal(epd, dst_dma_addr, val);
}
unlock_ret:
mutex_unlock(&ep->rma_info.rma_lock);
return err;
}
static int _scif_fence_wait(scif_epd_t epd, int mark)
{
struct scif_endpt *ep = (struct scif_endpt *)epd;
dma_cookie_t cookie = mark & ~SCIF_REMOTE_FENCE;
int err;
/* Wait for DMA callback in scif_fence_mark_cb(..) */
err = wait_event_interruptible_timeout(ep->rma_info.markwq,
dma_async_is_tx_complete(
ep->rma_info.dma_chan,
cookie, NULL, NULL) ==
DMA_COMPLETE,
SCIF_NODE_ALIVE_TIMEOUT);
if (!err)
err = -ETIMEDOUT;
else if (err > 0)
err = 0;
return err;
}
/**
* scif_rma_handle_remote_fences:
*
* This routine services remote fence requests.
*/
void scif_rma_handle_remote_fences(void)
{
struct list_head *item, *tmp;
struct scif_remote_fence_info *fence;
struct scif_endpt *ep;
int mark, err;
might_sleep();
mutex_lock(&scif_info.fencelock);
list_for_each_safe(item, tmp, &scif_info.fence) {
fence = list_entry(item, struct scif_remote_fence_info,
list);
/* Remove fence from global list */
list_del(&fence->list);
/* Initiate the fence operation */
ep = (struct scif_endpt *)fence->msg.payload[0];
mark = fence->msg.payload[2];
err = _scif_fence_wait(ep, mark);
if (err)
fence->msg.uop = SCIF_WAIT_NACK;
else
fence->msg.uop = SCIF_WAIT_ACK;
fence->msg.payload[0] = ep->remote_ep;
scif_nodeqp_send(ep->remote_dev, &fence->msg);
kfree(fence);
if (!atomic_sub_return(1, &ep->rma_info.fence_refcount))
schedule_work(&scif_info.misc_work);
}
mutex_unlock(&scif_info.fencelock);
}
static int _scif_send_fence(scif_epd_t epd, int uop, int mark, int *out_mark)
{
int err;
struct scifmsg msg;
struct scif_fence_info *fence_req;
struct scif_endpt *ep = (struct scif_endpt *)epd;
fence_req = kmalloc(sizeof(*fence_req), GFP_KERNEL);
if (!fence_req) {
err = -ENOMEM;
goto error;
}
fence_req->state = OP_IN_PROGRESS;
init_completion(&fence_req->comp);
msg.src = ep->port;
msg.uop = uop;
msg.payload[0] = ep->remote_ep;
msg.payload[1] = (u64)fence_req;
if (uop == SCIF_WAIT)
msg.payload[2] = mark;
spin_lock(&ep->lock);
if (ep->state == SCIFEP_CONNECTED)
err = scif_nodeqp_send(ep->remote_dev, &msg);
else
err = -ENOTCONN;
spin_unlock(&ep->lock);
if (err)
goto error_free;
retry:
/* Wait for a SCIF_WAIT_(N)ACK message */
err = wait_for_completion_timeout(&fence_req->comp,
SCIF_NODE_ALIVE_TIMEOUT);
if (!err && scifdev_alive(ep))
goto retry;
if (!err)
err = -ENODEV;
if (err > 0)
err = 0;
mutex_lock(&ep->rma_info.rma_lock);
if (err < 0) {
if (fence_req->state == OP_IN_PROGRESS)
fence_req->state = OP_FAILED;
}
if (fence_req->state == OP_FAILED && !err)
err = -ENOMEM;
if (uop == SCIF_MARK && fence_req->state == OP_COMPLETED)
*out_mark = SCIF_REMOTE_FENCE | fence_req->dma_mark;
mutex_unlock(&ep->rma_info.rma_lock);
error_free:
kfree(fence_req);
error:
return err;
}
/**
* scif_send_fence_mark:
* @epd: end point descriptor.
* @out_mark: Output DMA mark reported by peer.
*
* Send a remote fence mark request.
*/
static int scif_send_fence_mark(scif_epd_t epd, int *out_mark)
{
return _scif_send_fence(epd, SCIF_MARK, 0, out_mark);
}
/**
* scif_send_fence_wait:
* @epd: end point descriptor.
* @mark: DMA mark to wait for.
*
* Send a remote fence wait request.
*/
static int scif_send_fence_wait(scif_epd_t epd, int mark)
{
return _scif_send_fence(epd, SCIF_WAIT, mark, NULL);
}
static int _scif_send_fence_signal_wait(struct scif_endpt *ep,
struct scif_fence_info *fence_req)
{
int err;
retry:
/* Wait for a SCIF_SIG_(N)ACK message */
err = wait_for_completion_timeout(&fence_req->comp,
SCIF_NODE_ALIVE_TIMEOUT);
if (!err && scifdev_alive(ep))
goto retry;
if (!err)
err = -ENODEV;
if (err > 0)
err = 0;
if (err < 0) {
mutex_lock(&ep->rma_info.rma_lock);
if (fence_req->state == OP_IN_PROGRESS)
fence_req->state = OP_FAILED;
mutex_unlock(&ep->rma_info.rma_lock);
}
if (fence_req->state == OP_FAILED && !err)
err = -ENXIO;
return err;
}
/**
* scif_send_fence_signal:
* @epd - endpoint descriptor
* @loff - local offset
* @lval - local value to write to loffset
* @roff - remote offset
* @rval - remote value to write to roffset
* @flags - flags
*
* Sends a remote fence signal request
*/
static int scif_send_fence_signal(scif_epd_t epd, off_t roff, u64 rval,
off_t loff, u64 lval, int flags)
{
int err = 0;
struct scifmsg msg;
struct scif_fence_info *fence_req;
struct scif_endpt *ep = (struct scif_endpt *)epd;
fence_req = kmalloc(sizeof(*fence_req), GFP_KERNEL);
if (!fence_req) {
err = -ENOMEM;
goto error;
}
fence_req->state = OP_IN_PROGRESS;
init_completion(&fence_req->comp);
msg.src = ep->port;
if (flags & SCIF_SIGNAL_LOCAL) {
msg.uop = SCIF_SIG_LOCAL;
msg.payload[0] = ep->remote_ep;
msg.payload[1] = roff;
msg.payload[2] = rval;
msg.payload[3] = (u64)fence_req;
spin_lock(&ep->lock);
if (ep->state == SCIFEP_CONNECTED)
err = scif_nodeqp_send(ep->remote_dev, &msg);
else
err = -ENOTCONN;
spin_unlock(&ep->lock);
if (err)
goto error_free;
err = _scif_send_fence_signal_wait(ep, fence_req);
if (err)
goto error_free;
}
fence_req->state = OP_IN_PROGRESS;
if (flags & SCIF_SIGNAL_REMOTE) {
msg.uop = SCIF_SIG_REMOTE;
msg.payload[0] = ep->remote_ep;
msg.payload[1] = loff;
msg.payload[2] = lval;
msg.payload[3] = (u64)fence_req;
spin_lock(&ep->lock);
if (ep->state == SCIFEP_CONNECTED)
err = scif_nodeqp_send(ep->remote_dev, &msg);
else
err = -ENOTCONN;
spin_unlock(&ep->lock);
if (err)
goto error_free;
err = _scif_send_fence_signal_wait(ep, fence_req);
}
error_free:
kfree(fence_req);
error:
return err;
}
static void scif_fence_mark_cb(void *arg)
{
struct scif_endpt *ep = (struct scif_endpt *)arg;
wake_up_interruptible(&ep->rma_info.markwq);
atomic_dec(&ep->rma_info.fence_refcount);
}
/*
* _scif_fence_mark:
*
* @epd - endpoint descriptor
* Set up a mark for this endpoint and return the value of the mark.
*/
int _scif_fence_mark(scif_epd_t epd, int *mark)
{
struct scif_endpt *ep = (struct scif_endpt *)epd;
struct dma_chan *chan = ep->rma_info.dma_chan;
struct dma_device *ddev = chan->device;
struct dma_async_tx_descriptor *tx;
dma_cookie_t cookie;
int err;
tx = ddev->device_prep_dma_memcpy(chan, 0, 0, 0, DMA_PREP_FENCE);
if (!tx) {
err = -ENOMEM;
dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n",
__func__, __LINE__, err);
return err;
}
cookie = tx->tx_submit(tx);
if (dma_submit_error(cookie)) {
err = (int)cookie;
dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n",
__func__, __LINE__, err);
return err;
}
dma_async_issue_pending(chan);
tx = ddev->device_prep_dma_interrupt(chan, DMA_PREP_INTERRUPT);
if (!tx) {
err = -ENOMEM;
dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n",
__func__, __LINE__, err);
return err;
}
tx->callback = scif_fence_mark_cb;
tx->callback_param = ep;
*mark = cookie = tx->tx_submit(tx);
if (dma_submit_error(cookie)) {
err = (int)cookie;
dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n",
__func__, __LINE__, err);
return err;
}
atomic_inc(&ep->rma_info.fence_refcount);
dma_async_issue_pending(chan);
return 0;
}
#define SCIF_LOOPB_MAGIC_MARK 0xdead
int scif_fence_mark(scif_epd_t epd, int flags, int *mark)
{
struct scif_endpt *ep = (struct scif_endpt *)epd;
int err = 0;
dev_dbg(scif_info.mdev.this_device,
"SCIFAPI fence_mark: ep %p flags 0x%x mark 0x%x\n",
ep, flags, *mark);
err = scif_verify_epd(ep);
if (err)
return err;
/* Invalid flags? */
if (flags & ~(SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER))
return -EINVAL;
/* At least one of init self or peer RMA should be set */
if (!(flags & (SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER)))
return -EINVAL;
/* Exactly one of init self or peer RMA should be set but not both */
if ((flags & SCIF_FENCE_INIT_SELF) && (flags & SCIF_FENCE_INIT_PEER))
return -EINVAL;
/*
* Management node loopback does not need to use DMA.
* Return a valid mark to be symmetric.
*/
if (scifdev_self(ep->remote_dev) && scif_is_mgmt_node()) {
*mark = SCIF_LOOPB_MAGIC_MARK;
return 0;
}
if (flags & SCIF_FENCE_INIT_SELF)
err = _scif_fence_mark(epd, mark);
else
err = scif_send_fence_mark(ep, mark);
if (err)
dev_err(scif_info.mdev.this_device,
"%s %d err %d\n", __func__, __LINE__, err);
dev_dbg(scif_info.mdev.this_device,
"SCIFAPI fence_mark: ep %p flags 0x%x mark 0x%x err %d\n",
ep, flags, *mark, err);
return err;
}
EXPORT_SYMBOL_GPL(scif_fence_mark);
int scif_fence_wait(scif_epd_t epd, int mark)
{
struct scif_endpt *ep = (struct scif_endpt *)epd;
int err = 0;
dev_dbg(scif_info.mdev.this_device,
"SCIFAPI fence_wait: ep %p mark 0x%x\n",
ep, mark);
err = scif_verify_epd(ep);
if (err)
return err;
/*
* Management node loopback does not need to use DMA.
* The only valid mark provided is 0 so simply
* return success if the mark is valid.
*/
if (scifdev_self(ep->remote_dev) && scif_is_mgmt_node()) {
if (mark == SCIF_LOOPB_MAGIC_MARK)
return 0;
else
return -EINVAL;
}
if (mark & SCIF_REMOTE_FENCE)
err = scif_send_fence_wait(epd, mark);
else
err = _scif_fence_wait(epd, mark);
if (err < 0)
dev_err(scif_info.mdev.this_device,
"%s %d err %d\n", __func__, __LINE__, err);
return err;
}
EXPORT_SYMBOL_GPL(scif_fence_wait);
int scif_fence_signal(scif_epd_t epd, off_t loff, u64 lval,
off_t roff, u64 rval, int flags)
{
struct scif_endpt *ep = (struct scif_endpt *)epd;
int err = 0;
dev_dbg(scif_info.mdev.this_device,
"SCIFAPI fence_signal: ep %p loff 0x%lx lval 0x%llx roff 0x%lx rval 0x%llx flags 0x%x\n",
ep, loff, lval, roff, rval, flags);
err = scif_verify_epd(ep);
if (err)
return err;
/* Invalid flags? */
if (flags & ~(SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER |
SCIF_SIGNAL_LOCAL | SCIF_SIGNAL_REMOTE))
return -EINVAL;
/* At least one of init self or peer RMA should be set */
if (!(flags & (SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER)))
return -EINVAL;
/* Exactly one of init self or peer RMA should be set but not both */
if ((flags & SCIF_FENCE_INIT_SELF) && (flags & SCIF_FENCE_INIT_PEER))
return -EINVAL;
/* At least one of SCIF_SIGNAL_LOCAL or SCIF_SIGNAL_REMOTE required */
if (!(flags & (SCIF_SIGNAL_LOCAL | SCIF_SIGNAL_REMOTE)))
return -EINVAL;
/* Only Dword offsets allowed */
if ((flags & SCIF_SIGNAL_LOCAL) && (loff & (sizeof(u32) - 1)))
return -EINVAL;
/* Only Dword aligned offsets allowed */
if ((flags & SCIF_SIGNAL_REMOTE) && (roff & (sizeof(u32) - 1)))
return -EINVAL;
if (flags & SCIF_FENCE_INIT_PEER) {
err = scif_send_fence_signal(epd, roff, rval, loff,
lval, flags);
} else {
/* Local Signal in Local RAS */
if (flags & SCIF_SIGNAL_LOCAL) {
err = scif_prog_signal(epd, loff, lval,
SCIF_WINDOW_SELF);
if (err)
goto error_ret;
}
/* Signal in Remote RAS */
if (flags & SCIF_SIGNAL_REMOTE)
err = scif_prog_signal(epd, roff,
rval, SCIF_WINDOW_PEER);
}
error_ret:
if (err)
dev_err(scif_info.mdev.this_device,
"%s %d err %d\n", __func__, __LINE__, err);
return err;
}
EXPORT_SYMBOL_GPL(scif_fence_signal);