2019-05-27 14:55:21 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2015-09-30 09:16:19 +08:00
|
|
|
/*
|
|
|
|
* Intel MIC Platform Software Stack (MPSS)
|
|
|
|
*
|
|
|
|
* Copyright(c) 2015 Intel Corporation.
|
|
|
|
*
|
|
|
|
* 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];
|
2016-04-13 14:47:42 +08:00
|
|
|
int mark = 0;
|
|
|
|
int err;
|
2015-09-30 09:16:19 +08:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
misc: mic: fix a DMA pool free failure
In _scif_prog_signal(), a DMA pool is allocated if the MIC Coprocessor is
not X100, i.e., the boolean variable 'x100' is false. This DMA pool will be
freed eventually through the callback function scif_prog_signal_cb() with
the parameter of 'status', which actually points to the start of DMA pool.
Specifically, in scif_prog_signal_cb(), the 'ep' field and the
'src_dma_addr' field of 'status' are used to free the DMA pool by invoking
dma_pool_free(). Given that 'status' points to the start address of the DMA
pool, both 'status->ep' and 'status->src_dma_addr' are in the DMA pool. And
so, the device has the permission to access them. Even worse, a malicious
device can modify them. As a result, dma_pool_free() will not succeed.
To avoid the above issue, this patch introduces a new data structure, i.e.,
scif_cb_arg, to store the arguments required by the call back function. A
variable 'cb_arg' is allocated in _scif_prog_signal() to pass the
arguments. 'cb_arg' will be freed after dma_pool_free() in
scif_prog_signal_cb().
Signed-off-by: Wenwen Wang <wang6495@umn.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-04 23:16:41 +08:00
|
|
|
struct scif_cb_arg *cb_arg = arg;
|
2015-09-30 09:16:19 +08:00
|
|
|
|
misc: mic: fix a DMA pool free failure
In _scif_prog_signal(), a DMA pool is allocated if the MIC Coprocessor is
not X100, i.e., the boolean variable 'x100' is false. This DMA pool will be
freed eventually through the callback function scif_prog_signal_cb() with
the parameter of 'status', which actually points to the start of DMA pool.
Specifically, in scif_prog_signal_cb(), the 'ep' field and the
'src_dma_addr' field of 'status' are used to free the DMA pool by invoking
dma_pool_free(). Given that 'status' points to the start address of the DMA
pool, both 'status->ep' and 'status->src_dma_addr' are in the DMA pool. And
so, the device has the permission to access them. Even worse, a malicious
device can modify them. As a result, dma_pool_free() will not succeed.
To avoid the above issue, this patch introduces a new data structure, i.e.,
scif_cb_arg, to store the arguments required by the call back function. A
variable 'cb_arg' is allocated in _scif_prog_signal() to pass the
arguments. 'cb_arg' will be freed after dma_pool_free() in
scif_prog_signal_cb().
Signed-off-by: Wenwen Wang <wang6495@umn.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-04 23:16:41 +08:00
|
|
|
dma_pool_free(cb_arg->ep->remote_dev->signal_pool, cb_arg->status,
|
|
|
|
cb_arg->src_dma_addr);
|
|
|
|
kfree(cb_arg);
|
2015-09-30 09:16:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
misc: mic: fix a DMA pool free failure
In _scif_prog_signal(), a DMA pool is allocated if the MIC Coprocessor is
not X100, i.e., the boolean variable 'x100' is false. This DMA pool will be
freed eventually through the callback function scif_prog_signal_cb() with
the parameter of 'status', which actually points to the start of DMA pool.
Specifically, in scif_prog_signal_cb(), the 'ep' field and the
'src_dma_addr' field of 'status' are used to free the DMA pool by invoking
dma_pool_free(). Given that 'status' points to the start address of the DMA
pool, both 'status->ep' and 'status->src_dma_addr' are in the DMA pool. And
so, the device has the permission to access them. Even worse, a malicious
device can modify them. As a result, dma_pool_free() will not succeed.
To avoid the above issue, this patch introduces a new data structure, i.e.,
scif_cb_arg, to store the arguments required by the call back function. A
variable 'cb_arg' is allocated in _scif_prog_signal() to pass the
arguments. 'cb_arg' will be freed after dma_pool_free() in
scif_prog_signal_cb().
Signed-off-by: Wenwen Wang <wang6495@umn.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-04 23:16:41 +08:00
|
|
|
struct scif_cb_arg *cb_arg = NULL;
|
2015-09-30 09:16:19 +08:00
|
|
|
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) {
|
misc: mic: fix a DMA pool free failure
In _scif_prog_signal(), a DMA pool is allocated if the MIC Coprocessor is
not X100, i.e., the boolean variable 'x100' is false. This DMA pool will be
freed eventually through the callback function scif_prog_signal_cb() with
the parameter of 'status', which actually points to the start of DMA pool.
Specifically, in scif_prog_signal_cb(), the 'ep' field and the
'src_dma_addr' field of 'status' are used to free the DMA pool by invoking
dma_pool_free(). Given that 'status' points to the start address of the DMA
pool, both 'status->ep' and 'status->src_dma_addr' are in the DMA pool. And
so, the device has the permission to access them. Even worse, a malicious
device can modify them. As a result, dma_pool_free() will not succeed.
To avoid the above issue, this patch introduces a new data structure, i.e.,
scif_cb_arg, to store the arguments required by the call back function. A
variable 'cb_arg' is allocated in _scif_prog_signal() to pass the
arguments. 'cb_arg' will be freed after dma_pool_free() in
scif_prog_signal_cb().
Signed-off-by: Wenwen Wang <wang6495@umn.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-04 23:16:41 +08:00
|
|
|
cb_arg = kmalloc(sizeof(*cb_arg), GFP_KERNEL);
|
|
|
|
if (!cb_arg) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto dma_fail;
|
|
|
|
}
|
|
|
|
cb_arg->src_dma_addr = src;
|
|
|
|
cb_arg->status = status;
|
|
|
|
cb_arg->ep = ep;
|
2015-09-30 09:16:19 +08:00
|
|
|
tx->callback = scif_prog_signal_cb;
|
misc: mic: fix a DMA pool free failure
In _scif_prog_signal(), a DMA pool is allocated if the MIC Coprocessor is
not X100, i.e., the boolean variable 'x100' is false. This DMA pool will be
freed eventually through the callback function scif_prog_signal_cb() with
the parameter of 'status', which actually points to the start of DMA pool.
Specifically, in scif_prog_signal_cb(), the 'ep' field and the
'src_dma_addr' field of 'status' are used to free the DMA pool by invoking
dma_pool_free(). Given that 'status' points to the start address of the DMA
pool, both 'status->ep' and 'status->src_dma_addr' are in the DMA pool. And
so, the device has the permission to access them. Even worse, a malicious
device can modify them. As a result, dma_pool_free() will not succeed.
To avoid the above issue, this patch introduces a new data structure, i.e.,
scif_cb_arg, to store the arguments required by the call back function. A
variable 'cb_arg' is allocated in _scif_prog_signal() to pass the
arguments. 'cb_arg' will be freed after dma_pool_free() in
scif_prog_signal_cb().
Signed-off-by: Wenwen Wang <wang6495@umn.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-04 23:16:41 +08:00
|
|
|
tx->callback_param = cb_arg;
|
2015-09-30 09:16:19 +08:00
|
|
|
}
|
|
|
|
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:
|
misc: mic: fix a DMA pool free failure
In _scif_prog_signal(), a DMA pool is allocated if the MIC Coprocessor is
not X100, i.e., the boolean variable 'x100' is false. This DMA pool will be
freed eventually through the callback function scif_prog_signal_cb() with
the parameter of 'status', which actually points to the start of DMA pool.
Specifically, in scif_prog_signal_cb(), the 'ep' field and the
'src_dma_addr' field of 'status' are used to free the DMA pool by invoking
dma_pool_free(). Given that 'status' points to the start address of the DMA
pool, both 'status->ep' and 'status->src_dma_addr' are in the DMA pool. And
so, the device has the permission to access them. Even worse, a malicious
device can modify them. As a result, dma_pool_free() will not succeed.
To avoid the above issue, this patch introduces a new data structure, i.e.,
scif_cb_arg, to store the arguments required by the call back function. A
variable 'cb_arg' is allocated in _scif_prog_signal() to pass the
arguments. 'cb_arg' will be freed after dma_pool_free() in
scif_prog_signal_cb().
Signed-off-by: Wenwen Wang <wang6495@umn.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-04 23:16:41 +08:00
|
|
|
if (!x100) {
|
2015-09-30 09:16:19 +08:00
|
|
|
dma_pool_free(ep->remote_dev->signal_pool, status,
|
misc: mic: fix a DMA pool free failure
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>
2018-10-11 07:38:28 +08:00
|
|
|
src - offsetof(struct scif_status, val));
|
misc: mic: fix a DMA pool free failure
In _scif_prog_signal(), a DMA pool is allocated if the MIC Coprocessor is
not X100, i.e., the boolean variable 'x100' is false. This DMA pool will be
freed eventually through the callback function scif_prog_signal_cb() with
the parameter of 'status', which actually points to the start of DMA pool.
Specifically, in scif_prog_signal_cb(), the 'ep' field and the
'src_dma_addr' field of 'status' are used to free the DMA pool by invoking
dma_pool_free(). Given that 'status' points to the start address of the DMA
pool, both 'status->ep' and 'status->src_dma_addr' are in the DMA pool. And
so, the device has the permission to access them. Even worse, a malicious
device can modify them. As a result, dma_pool_free() will not succeed.
To avoid the above issue, this patch introduces a new data structure, i.e.,
scif_cb_arg, to store the arguments required by the call back function. A
variable 'cb_arg' is allocated in _scif_prog_signal() to pass the
arguments. 'cb_arg' will be freed after dma_pool_free() in
scif_prog_signal_cb().
Signed-off-by: Wenwen Wang <wang6495@umn.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-12-04 23:16:41 +08:00
|
|
|
kfree(cb_arg);
|
|
|
|
}
|
2015-09-30 09:16:19 +08:00
|
|
|
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);
|