blob: 7bb929f05d85227b1f6b16f5d037ac58ad3e2200 [file] [log] [blame]
/*
* 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);