diff options
| author | Sudeep Dutt <sudeep.dutt@intel.com> | 2015-09-29 21:16:19 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-10-04 07:54:54 -0400 |
| commit | 564c8d8dfc39638978ac37d37f61733240f31a36 (patch) | |
| tree | 19d428166bf32aa08ee8901f5cf6baeca6d8b269 /drivers/misc/mic | |
| parent | 7cc31cd27752e5e92f968e39095c4ea9d78c758e (diff) | |
misc: mic: SCIF fence
This patch implements the fence APIs required to synchronize
DMAs. SCIF provides an interface to return a "mark" for all DMAs
programmed at the instant the API was called. Users can then "wait" on
the mark provided previously by blocking inside the kernel. Upon
receipt of a DMA completion interrupt the waiting thread is woken
up. There is also an interface to signal DMA completion by polling for
a location to be updated via a "signal" cookie to avoid the interrupt
overhead in the mark/wait interface. SCIF allows programming fences on
both the local and the remote node for both the mark/wait or the fence
signal APIs.
Reviewed-by: Ashutosh Dixit <ashutosh.dixit@intel.com>
Reviewed-by: Nikhil Rao <nikhil.rao@intel.com>
Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@intel.com>
Signed-off-by: Sudeep Dutt <sudeep.dutt@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc/mic')
| -rw-r--r-- | drivers/misc/mic/scif/scif_fence.c | 771 |
1 files changed, 771 insertions, 0 deletions
diff --git a/drivers/misc/mic/scif/scif_fence.c b/drivers/misc/mic/scif/scif_fence.c new file mode 100644 index 000000000000..7f2c96f57066 --- /dev/null +++ b/drivers/misc/mic/scif/scif_fence.c | |||
| @@ -0,0 +1,771 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2015 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * Intel SCIF driver. | ||
| 16 | * | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include "scif_main.h" | ||
| 20 | |||
| 21 | /** | ||
| 22 | * scif_recv_mark: Handle SCIF_MARK request | ||
| 23 | * @msg: Interrupt message | ||
| 24 | * | ||
| 25 | * The peer has requested a mark. | ||
| 26 | */ | ||
| 27 | void scif_recv_mark(struct scif_dev *scifdev, struct scifmsg *msg) | ||
| 28 | { | ||
| 29 | struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; | ||
| 30 | int mark, err; | ||
| 31 | |||
| 32 | err = _scif_fence_mark(ep, &mark); | ||
| 33 | if (err) | ||
| 34 | msg->uop = SCIF_MARK_NACK; | ||
| 35 | else | ||
| 36 | msg->uop = SCIF_MARK_ACK; | ||
| 37 | msg->payload[0] = ep->remote_ep; | ||
| 38 | msg->payload[2] = mark; | ||
| 39 | scif_nodeqp_send(ep->remote_dev, msg); | ||
| 40 | } | ||
| 41 | |||
| 42 | /** | ||
| 43 | * scif_recv_mark_resp: Handle SCIF_MARK_(N)ACK messages. | ||
| 44 | * @msg: Interrupt message | ||
| 45 | * | ||
| 46 | * The peer has responded to a SCIF_MARK message. | ||
| 47 | */ | ||
| 48 | void scif_recv_mark_resp(struct scif_dev *scifdev, struct scifmsg *msg) | ||
| 49 | { | ||
| 50 | struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; | ||
| 51 | struct scif_fence_info *fence_req = | ||
| 52 | (struct scif_fence_info *)msg->payload[1]; | ||
| 53 | |||
| 54 | mutex_lock(&ep->rma_info.rma_lock); | ||
| 55 | if (msg->uop == SCIF_MARK_ACK) { | ||
| 56 | fence_req->state = OP_COMPLETED; | ||
| 57 | fence_req->dma_mark = (int)msg->payload[2]; | ||
| 58 | } else { | ||
| 59 | fence_req->state = OP_FAILED; | ||
| 60 | } | ||
| 61 | mutex_unlock(&ep->rma_info.rma_lock); | ||
| 62 | complete(&fence_req->comp); | ||
| 63 | } | ||
| 64 | |||
| 65 | /** | ||
| 66 | * scif_recv_wait: Handle SCIF_WAIT request | ||
| 67 | * @msg: Interrupt message | ||
| 68 | * | ||
| 69 | * The peer has requested waiting on a fence. | ||
| 70 | */ | ||
| 71 | void scif_recv_wait(struct scif_dev *scifdev, struct scifmsg *msg) | ||
| 72 | { | ||
| 73 | struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; | ||
| 74 | struct scif_remote_fence_info *fence; | ||
| 75 | |||
| 76 | /* | ||
| 77 | * Allocate structure for remote fence information and | ||
| 78 | * send a NACK if the allocation failed. The peer will | ||
| 79 | * return ENOMEM upon receiving a NACK. | ||
| 80 | */ | ||
| 81 | fence = kmalloc(sizeof(*fence), GFP_KERNEL); | ||
| 82 | if (!fence) { | ||
| 83 | msg->payload[0] = ep->remote_ep; | ||
| 84 | msg->uop = SCIF_WAIT_NACK; | ||
| 85 | scif_nodeqp_send(ep->remote_dev, msg); | ||
| 86 | return; | ||
| 87 | } | ||
| 88 | |||
| 89 | /* Prepare the fence request */ | ||
| 90 | memcpy(&fence->msg, msg, sizeof(struct scifmsg)); | ||
| 91 | INIT_LIST_HEAD(&fence->list); | ||
| 92 | |||
| 93 | /* Insert to the global remote fence request list */ | ||
| 94 | mutex_lock(&scif_info.fencelock); | ||
| 95 | atomic_inc(&ep->rma_info.fence_refcount); | ||
| 96 | list_add_tail(&fence->list, &scif_info.fence); | ||
| 97 | mutex_unlock(&scif_info.fencelock); | ||
| 98 | |||
| 99 | schedule_work(&scif_info.misc_work); | ||
| 100 | } | ||
| 101 | |||
| 102 | /** | ||
| 103 | * scif_recv_wait_resp: Handle SCIF_WAIT_(N)ACK messages. | ||
| 104 | * @msg: Interrupt message | ||
| 105 | * | ||
| 106 | * The peer has responded to a SCIF_WAIT message. | ||
| 107 | */ | ||
| 108 | void scif_recv_wait_resp(struct scif_dev *scifdev, struct scifmsg *msg) | ||
| 109 | { | ||
| 110 | struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; | ||
| 111 | struct scif_fence_info *fence_req = | ||
| 112 | (struct scif_fence_info *)msg->payload[1]; | ||
| 113 | |||
| 114 | mutex_lock(&ep->rma_info.rma_lock); | ||
| 115 | if (msg->uop == SCIF_WAIT_ACK) | ||
| 116 | fence_req->state = OP_COMPLETED; | ||
| 117 | else | ||
| 118 | fence_req->state = OP_FAILED; | ||
| 119 | mutex_unlock(&ep->rma_info.rma_lock); | ||
| 120 | complete(&fence_req->comp); | ||
| 121 | } | ||
| 122 | |||
| 123 | /** | ||
| 124 | * scif_recv_sig_local: Handle SCIF_SIG_LOCAL request | ||
| 125 | * @msg: Interrupt message | ||
| 126 | * | ||
| 127 | * The peer has requested a signal on a local offset. | ||
| 128 | */ | ||
| 129 | void scif_recv_sig_local(struct scif_dev *scifdev, struct scifmsg *msg) | ||
| 130 | { | ||
| 131 | struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; | ||
| 132 | int err; | ||
| 133 | |||
| 134 | err = scif_prog_signal(ep, msg->payload[1], msg->payload[2], | ||
| 135 | SCIF_WINDOW_SELF); | ||
| 136 | if (err) | ||
| 137 | msg->uop = SCIF_SIG_NACK; | ||
| 138 | else | ||
| 139 | msg->uop = SCIF_SIG_ACK; | ||
| 140 | msg->payload[0] = ep->remote_ep; | ||
| 141 | scif_nodeqp_send(ep->remote_dev, msg); | ||
| 142 | } | ||
| 143 | |||
| 144 | /** | ||
| 145 | * scif_recv_sig_remote: Handle SCIF_SIGNAL_REMOTE request | ||
| 146 | * @msg: Interrupt message | ||
| 147 | * | ||
| 148 | * The peer has requested a signal on a remote offset. | ||
| 149 | */ | ||
| 150 | void scif_recv_sig_remote(struct scif_dev *scifdev, struct scifmsg *msg) | ||
| 151 | { | ||
| 152 | struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; | ||
| 153 | int err; | ||
| 154 | |||
| 155 | err = scif_prog_signal(ep, msg->payload[1], msg->payload[2], | ||
| 156 | SCIF_WINDOW_PEER); | ||
| 157 | if (err) | ||
| 158 | msg->uop = SCIF_SIG_NACK; | ||
| 159 | else | ||
| 160 | msg->uop = SCIF_SIG_ACK; | ||
| 161 | msg->payload[0] = ep->remote_ep; | ||
| 162 | scif_nodeqp_send(ep->remote_dev, msg); | ||
| 163 | } | ||
| 164 | |||
| 165 | /** | ||
| 166 | * scif_recv_sig_resp: Handle SCIF_SIG_(N)ACK messages. | ||
| 167 | * @msg: Interrupt message | ||
| 168 | * | ||
| 169 | * The peer has responded to a signal request. | ||
| 170 | */ | ||
| 171 | void scif_recv_sig_resp(struct scif_dev *scifdev, struct scifmsg *msg) | ||
| 172 | { | ||
| 173 | struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; | ||
| 174 | struct scif_fence_info *fence_req = | ||
| 175 | (struct scif_fence_info *)msg->payload[3]; | ||
| 176 | |||
| 177 | mutex_lock(&ep->rma_info.rma_lock); | ||
| 178 | if (msg->uop == SCIF_SIG_ACK) | ||
| 179 | fence_req->state = OP_COMPLETED; | ||
| 180 | else | ||
| 181 | fence_req->state = OP_FAILED; | ||
| 182 | mutex_unlock(&ep->rma_info.rma_lock); | ||
| 183 | complete(&fence_req->comp); | ||
| 184 | } | ||
| 185 | |||
| 186 | static inline void *scif_get_local_va(off_t off, struct scif_window *window) | ||
| 187 | { | ||
| 188 | struct page **pages = window->pinned_pages->pages; | ||
| 189 | int page_nr = (off - window->offset) >> PAGE_SHIFT; | ||
| 190 | off_t page_off = off & ~PAGE_MASK; | ||
| 191 | |||
| 192 | return page_address(pages[page_nr]) + page_off; | ||
| 193 | } | ||
| 194 | |||
| 195 | static void scif_prog_signal_cb(void *arg) | ||
| 196 | { | ||
| 197 | struct scif_status *status = arg; | ||
| 198 | |||
| 199 | dma_pool_free(status->ep->remote_dev->signal_pool, status, | ||
| 200 | status->src_dma_addr); | ||
| 201 | } | ||
| 202 | |||
| 203 | static int _scif_prog_signal(scif_epd_t epd, dma_addr_t dst, u64 val) | ||
| 204 | { | ||
| 205 | struct scif_endpt *ep = (struct scif_endpt *)epd; | ||
| 206 | struct dma_chan *chan = ep->rma_info.dma_chan; | ||
| 207 | struct dma_device *ddev = chan->device; | ||
| 208 | bool x100 = !is_dma_copy_aligned(chan->device, 1, 1, 1); | ||
| 209 | struct dma_async_tx_descriptor *tx; | ||
| 210 | struct scif_status *status = NULL; | ||
| 211 | dma_addr_t src; | ||
| 212 | dma_cookie_t cookie; | ||
| 213 | int err; | ||
| 214 | |||
| 215 | tx = ddev->device_prep_dma_memcpy(chan, 0, 0, 0, DMA_PREP_FENCE); | ||
| 216 | if (!tx) { | ||
| 217 | err = -ENOMEM; | ||
| 218 | dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", | ||
| 219 | __func__, __LINE__, err); | ||
| 220 | goto alloc_fail; | ||
| 221 | } | ||
| 222 | cookie = tx->tx_submit(tx); | ||
| 223 | if (dma_submit_error(cookie)) { | ||
| 224 | err = (int)cookie; | ||
| 225 | dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", | ||
| 226 | __func__, __LINE__, err); | ||
| 227 | goto alloc_fail; | ||
| 228 | } | ||
| 229 | dma_async_issue_pending(chan); | ||
| 230 | if (x100) { | ||
| 231 | /* | ||
| 232 | * For X100 use the status descriptor to write the value to | ||
| 233 | * the destination. | ||
| 234 | */ | ||
| 235 | tx = ddev->device_prep_dma_imm_data(chan, dst, val, 0); | ||
| 236 | } else { | ||
| 237 | status = dma_pool_alloc(ep->remote_dev->signal_pool, GFP_KERNEL, | ||
| 238 | &src); | ||
| 239 | if (!status) { | ||
| 240 | err = -ENOMEM; | ||
| 241 | dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", | ||
| 242 | __func__, __LINE__, err); | ||
| 243 | goto alloc_fail; | ||
| 244 | } | ||
| 245 | status->val = val; | ||
| 246 | status->src_dma_addr = src; | ||
| 247 | status->ep = ep; | ||
| 248 | src += offsetof(struct scif_status, val); | ||
| 249 | tx = ddev->device_prep_dma_memcpy(chan, dst, src, sizeof(val), | ||
| 250 | DMA_PREP_INTERRUPT); | ||
| 251 | } | ||
| 252 | if (!tx) { | ||
| 253 | err = -ENOMEM; | ||
| 254 | dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", | ||
| 255 | __func__, __LINE__, err); | ||
| 256 | goto dma_fail; | ||
| 257 | } | ||
| 258 | if (!x100) { | ||
| 259 | tx->callback = scif_prog_signal_cb; | ||
| 260 | tx->callback_param = status; | ||
| 261 | } | ||
| 262 | cookie = tx->tx_submit(tx); | ||
| 263 | if (dma_submit_error(cookie)) { | ||
| 264 | err = -EIO; | ||
| 265 | dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", | ||
| 266 | __func__, __LINE__, err); | ||
| 267 | goto dma_fail; | ||
| 268 | } | ||
| 269 | dma_async_issue_pending(chan); | ||
| 270 | return 0; | ||
| 271 | dma_fail: | ||
| 272 | if (!x100) | ||
| 273 | dma_pool_free(ep->remote_dev->signal_pool, status, | ||
| 274 | status->src_dma_addr); | ||
| 275 | alloc_fail: | ||
| 276 | return err; | ||
| 277 | } | ||
| 278 | |||
| 279 | /* | ||
| 280 | * scif_prog_signal: | ||
| 281 | * @epd - Endpoint Descriptor | ||
| 282 | * @offset - registered address to write @val to | ||
| 283 | * @val - Value to be written at @offset | ||
| 284 | * @type - Type of the window. | ||
| 285 | * | ||
| 286 | * Arrange to write a value to the registered offset after ensuring that the | ||
| 287 | * offset provided is indeed valid. | ||
| 288 | */ | ||
| 289 | int scif_prog_signal(scif_epd_t epd, off_t offset, u64 val, | ||
| 290 | enum scif_window_type type) | ||
| 291 | { | ||
| 292 | struct scif_endpt *ep = (struct scif_endpt *)epd; | ||
| 293 | struct scif_window *window = NULL; | ||
| 294 | struct scif_rma_req req; | ||
| 295 | dma_addr_t dst_dma_addr; | ||
| 296 | int err; | ||
| 297 | |||
| 298 | mutex_lock(&ep->rma_info.rma_lock); | ||
| 299 | req.out_window = &window; | ||
| 300 | req.offset = offset; | ||
| 301 | req.nr_bytes = sizeof(u64); | ||
| 302 | req.prot = SCIF_PROT_WRITE; | ||
| 303 | req.type = SCIF_WINDOW_SINGLE; | ||
| 304 | if (type == SCIF_WINDOW_SELF) | ||
| 305 | req.head = &ep->rma_info.reg_list; | ||
| 306 | else | ||
| 307 | req.head = &ep->rma_info.remote_reg_list; | ||
| 308 | /* Does a valid window exist? */ | ||
| 309 | err = scif_query_window(&req); | ||
| 310 | if (err) { | ||
| 311 | dev_err(scif_info.mdev.this_device, | ||
| 312 | "%s %d err %d\n", __func__, __LINE__, err); | ||
| 313 | goto unlock_ret; | ||
| 314 | } | ||
| 315 | |||
| 316 | if (scif_is_mgmt_node() && scifdev_self(ep->remote_dev)) { | ||
| 317 | u64 *dst_virt; | ||
| 318 | |||
| 319 | if (type == SCIF_WINDOW_SELF) | ||
| 320 | dst_virt = scif_get_local_va(offset, window); | ||
| 321 | else | ||
| 322 | dst_virt = | ||
| 323 | scif_get_local_va(offset, (struct scif_window *) | ||
| 324 | window->peer_window); | ||
| 325 | *dst_virt = val; | ||
| 326 | } else { | ||
| 327 | dst_dma_addr = __scif_off_to_dma_addr(window, offset); | ||
| 328 | err = _scif_prog_signal(epd, dst_dma_addr, val); | ||
| 329 | } | ||
| 330 | unlock_ret: | ||
| 331 | mutex_unlock(&ep->rma_info.rma_lock); | ||
| 332 | return err; | ||
| 333 | } | ||
| 334 | |||
| 335 | static int _scif_fence_wait(scif_epd_t epd, int mark) | ||
| 336 | { | ||
| 337 | struct scif_endpt *ep = (struct scif_endpt *)epd; | ||
| 338 | dma_cookie_t cookie = mark & ~SCIF_REMOTE_FENCE; | ||
| 339 | int err; | ||
| 340 | |||
| 341 | /* Wait for DMA callback in scif_fence_mark_cb(..) */ | ||
| 342 | err = wait_event_interruptible_timeout(ep->rma_info.markwq, | ||
| 343 | dma_async_is_tx_complete( | ||
| 344 | ep->rma_info.dma_chan, | ||
| 345 | cookie, NULL, NULL) == | ||
| 346 | DMA_COMPLETE, | ||
| 347 | SCIF_NODE_ALIVE_TIMEOUT); | ||
| 348 | if (!err) | ||
| 349 | err = -ETIMEDOUT; | ||
| 350 | else if (err > 0) | ||
| 351 | err = 0; | ||
| 352 | return err; | ||
| 353 | } | ||
| 354 | |||
| 355 | /** | ||
| 356 | * scif_rma_handle_remote_fences: | ||
| 357 | * | ||
| 358 | * This routine services remote fence requests. | ||
| 359 | */ | ||
| 360 | void scif_rma_handle_remote_fences(void) | ||
| 361 | { | ||
| 362 | struct list_head *item, *tmp; | ||
| 363 | struct scif_remote_fence_info *fence; | ||
| 364 | struct scif_endpt *ep; | ||
| 365 | int mark, err; | ||
| 366 | |||
| 367 | might_sleep(); | ||
| 368 | mutex_lock(&scif_info.fencelock); | ||
| 369 | list_for_each_safe(item, tmp, &scif_info.fence) { | ||
| 370 | fence = list_entry(item, struct scif_remote_fence_info, | ||
| 371 | list); | ||
| 372 | /* Remove fence from global list */ | ||
| 373 | list_del(&fence->list); | ||
| 374 | |||
| 375 | /* Initiate the fence operation */ | ||
| 376 | ep = (struct scif_endpt *)fence->msg.payload[0]; | ||
| 377 | mark = fence->msg.payload[2]; | ||
| 378 | err = _scif_fence_wait(ep, mark); | ||
| 379 | if (err) | ||
| 380 | fence->msg.uop = SCIF_WAIT_NACK; | ||
| 381 | else | ||
| 382 | fence->msg.uop = SCIF_WAIT_ACK; | ||
| 383 | fence->msg.payload[0] = ep->remote_ep; | ||
| 384 | scif_nodeqp_send(ep->remote_dev, &fence->msg); | ||
| 385 | kfree(fence); | ||
| 386 | if (!atomic_sub_return(1, &ep->rma_info.fence_refcount)) | ||
| 387 | schedule_work(&scif_info.misc_work); | ||
| 388 | } | ||
| 389 | mutex_unlock(&scif_info.fencelock); | ||
| 390 | } | ||
| 391 | |||
| 392 | static int _scif_send_fence(scif_epd_t epd, int uop, int mark, int *out_mark) | ||
| 393 | { | ||
| 394 | int err; | ||
| 395 | struct scifmsg msg; | ||
| 396 | struct scif_fence_info *fence_req; | ||
| 397 | struct scif_endpt *ep = (struct scif_endpt *)epd; | ||
| 398 | |||
| 399 | fence_req = kmalloc(sizeof(*fence_req), GFP_KERNEL); | ||
| 400 | if (!fence_req) { | ||
| 401 | err = -ENOMEM; | ||
| 402 | goto error; | ||
| 403 | } | ||
| 404 | |||
| 405 | fence_req->state = OP_IN_PROGRESS; | ||
| 406 | init_completion(&fence_req->comp); | ||
| 407 | |||
| 408 | msg.src = ep->port; | ||
| 409 | msg.uop = uop; | ||
| 410 | msg.payload[0] = ep->remote_ep; | ||
| 411 | msg.payload[1] = (u64)fence_req; | ||
| 412 | if (uop == SCIF_WAIT) | ||
| 413 | msg.payload[2] = mark; | ||
| 414 | spin_lock(&ep->lock); | ||
| 415 | if (ep->state == SCIFEP_CONNECTED) | ||
| 416 | err = scif_nodeqp_send(ep->remote_dev, &msg); | ||
| 417 | else | ||
| 418 | err = -ENOTCONN; | ||
| 419 | spin_unlock(&ep->lock); | ||
| 420 | if (err) | ||
| 421 | goto error_free; | ||
| 422 | retry: | ||
| 423 | /* Wait for a SCIF_WAIT_(N)ACK message */ | ||
| 424 | err = wait_for_completion_timeout(&fence_req->comp, | ||
| 425 | SCIF_NODE_ALIVE_TIMEOUT); | ||
| 426 | if (!err && scifdev_alive(ep)) | ||
| 427 | goto retry; | ||
| 428 | if (!err) | ||
| 429 | err = -ENODEV; | ||
| 430 | if (err > 0) | ||
| 431 | err = 0; | ||
| 432 | mutex_lock(&ep->rma_info.rma_lock); | ||
| 433 | if (err < 0) { | ||
| 434 | if (fence_req->state == OP_IN_PROGRESS) | ||
| 435 | fence_req->state = OP_FAILED; | ||
| 436 | } | ||
| 437 | if (fence_req->state == OP_FAILED && !err) | ||
| 438 | err = -ENOMEM; | ||
| 439 | if (uop == SCIF_MARK && fence_req->state == OP_COMPLETED) | ||
| 440 | *out_mark = SCIF_REMOTE_FENCE | fence_req->dma_mark; | ||
| 441 | mutex_unlock(&ep->rma_info.rma_lock); | ||
| 442 | error_free: | ||
| 443 | kfree(fence_req); | ||
| 444 | error: | ||
| 445 | return err; | ||
| 446 | } | ||
| 447 | |||
| 448 | /** | ||
| 449 | * scif_send_fence_mark: | ||
| 450 | * @epd: end point descriptor. | ||
| 451 | * @out_mark: Output DMA mark reported by peer. | ||
| 452 | * | ||
| 453 | * Send a remote fence mark request. | ||
| 454 | */ | ||
| 455 | static int scif_send_fence_mark(scif_epd_t epd, int *out_mark) | ||
| 456 | { | ||
| 457 | return _scif_send_fence(epd, SCIF_MARK, 0, out_mark); | ||
| 458 | } | ||
| 459 | |||
| 460 | /** | ||
| 461 | * scif_send_fence_wait: | ||
| 462 | * @epd: end point descriptor. | ||
| 463 | * @mark: DMA mark to wait for. | ||
| 464 | * | ||
| 465 | * Send a remote fence wait request. | ||
| 466 | */ | ||
| 467 | static int scif_send_fence_wait(scif_epd_t epd, int mark) | ||
| 468 | { | ||
| 469 | return _scif_send_fence(epd, SCIF_WAIT, mark, NULL); | ||
| 470 | } | ||
| 471 | |||
| 472 | static int _scif_send_fence_signal_wait(struct scif_endpt *ep, | ||
| 473 | struct scif_fence_info *fence_req) | ||
| 474 | { | ||
| 475 | int err; | ||
| 476 | |||
| 477 | retry: | ||
| 478 | /* Wait for a SCIF_SIG_(N)ACK message */ | ||
| 479 | err = wait_for_completion_timeout(&fence_req->comp, | ||
| 480 | SCIF_NODE_ALIVE_TIMEOUT); | ||
| 481 | if (!err && scifdev_alive(ep)) | ||
| 482 | goto retry; | ||
| 483 | if (!err) | ||
| 484 | err = -ENODEV; | ||
| 485 | if (err > 0) | ||
| 486 | err = 0; | ||
| 487 | if (err < 0) { | ||
| 488 | mutex_lock(&ep->rma_info.rma_lock); | ||
| 489 | if (fence_req->state == OP_IN_PROGRESS) | ||
| 490 | fence_req->state = OP_FAILED; | ||
| 491 | mutex_unlock(&ep->rma_info.rma_lock); | ||
| 492 | } | ||
| 493 | if (fence_req->state == OP_FAILED && !err) | ||
| 494 | err = -ENXIO; | ||
| 495 | return err; | ||
| 496 | } | ||
| 497 | |||
| 498 | /** | ||
| 499 | * scif_send_fence_signal: | ||
| 500 | * @epd - endpoint descriptor | ||
| 501 | * @loff - local offset | ||
| 502 | * @lval - local value to write to loffset | ||
| 503 | * @roff - remote offset | ||
| 504 | * @rval - remote value to write to roffset | ||
| 505 | * @flags - flags | ||
| 506 | * | ||
| 507 | * Sends a remote fence signal request | ||
| 508 | */ | ||
| 509 | static int scif_send_fence_signal(scif_epd_t epd, off_t roff, u64 rval, | ||
| 510 | off_t loff, u64 lval, int flags) | ||
| 511 | { | ||
| 512 | int err = 0; | ||
| 513 | struct scifmsg msg; | ||
| 514 | struct scif_fence_info *fence_req; | ||
| 515 | struct scif_endpt *ep = (struct scif_endpt *)epd; | ||
| 516 | |||
| 517 | fence_req = kmalloc(sizeof(*fence_req), GFP_KERNEL); | ||
| 518 | if (!fence_req) { | ||
| 519 | err = -ENOMEM; | ||
| 520 | goto error; | ||
| 521 | } | ||
| 522 | |||
| 523 | fence_req->state = OP_IN_PROGRESS; | ||
| 524 | init_completion(&fence_req->comp); | ||
| 525 | msg.src = ep->port; | ||
| 526 | if (flags & SCIF_SIGNAL_LOCAL) { | ||
| 527 | msg.uop = SCIF_SIG_LOCAL; | ||
| 528 | msg.payload[0] = ep->remote_ep; | ||
| 529 | msg.payload[1] = roff; | ||
| 530 | msg.payload[2] = rval; | ||
| 531 | msg.payload[3] = (u64)fence_req; | ||
| 532 | spin_lock(&ep->lock); | ||
| 533 | if (ep->state == SCIFEP_CONNECTED) | ||
| 534 | err = scif_nodeqp_send(ep->remote_dev, &msg); | ||
| 535 | else | ||
| 536 | err = -ENOTCONN; | ||
| 537 | spin_unlock(&ep->lock); | ||
| 538 | if (err) | ||
| 539 | goto error_free; | ||
| 540 | err = _scif_send_fence_signal_wait(ep, fence_req); | ||
| 541 | if (err) | ||
| 542 | goto error_free; | ||
| 543 | } | ||
| 544 | fence_req->state = OP_IN_PROGRESS; | ||
| 545 | |||
| 546 | if (flags & SCIF_SIGNAL_REMOTE) { | ||
| 547 | msg.uop = SCIF_SIG_REMOTE; | ||
| 548 | msg.payload[0] = ep->remote_ep; | ||
| 549 | msg.payload[1] = loff; | ||
| 550 | msg.payload[2] = lval; | ||
| 551 | msg.payload[3] = (u64)fence_req; | ||
| 552 | spin_lock(&ep->lock); | ||
| 553 | if (ep->state == SCIFEP_CONNECTED) | ||
| 554 | err = scif_nodeqp_send(ep->remote_dev, &msg); | ||
| 555 | else | ||
| 556 | err = -ENOTCONN; | ||
| 557 | spin_unlock(&ep->lock); | ||
| 558 | if (err) | ||
| 559 | goto error_free; | ||
| 560 | err = _scif_send_fence_signal_wait(ep, fence_req); | ||
| 561 | } | ||
| 562 | error_free: | ||
| 563 | kfree(fence_req); | ||
| 564 | error: | ||
| 565 | return err; | ||
| 566 | } | ||
| 567 | |||
| 568 | static void scif_fence_mark_cb(void *arg) | ||
| 569 | { | ||
| 570 | struct scif_endpt *ep = (struct scif_endpt *)arg; | ||
| 571 | |||
| 572 | wake_up_interruptible(&ep->rma_info.markwq); | ||
| 573 | atomic_dec(&ep->rma_info.fence_refcount); | ||
| 574 | } | ||
| 575 | |||
| 576 | /* | ||
| 577 | * _scif_fence_mark: | ||
| 578 | * | ||
| 579 | * @epd - endpoint descriptor | ||
| 580 | * Set up a mark for this endpoint and return the value of the mark. | ||
| 581 | */ | ||
| 582 | int _scif_fence_mark(scif_epd_t epd, int *mark) | ||
| 583 | { | ||
| 584 | struct scif_endpt *ep = (struct scif_endpt *)epd; | ||
| 585 | struct dma_chan *chan = ep->rma_info.dma_chan; | ||
| 586 | struct dma_device *ddev = chan->device; | ||
| 587 | struct dma_async_tx_descriptor *tx; | ||
| 588 | dma_cookie_t cookie; | ||
| 589 | int err; | ||
| 590 | |||
| 591 | tx = ddev->device_prep_dma_memcpy(chan, 0, 0, 0, DMA_PREP_FENCE); | ||
| 592 | if (!tx) { | ||
| 593 | err = -ENOMEM; | ||
| 594 | dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", | ||
| 595 | __func__, __LINE__, err); | ||
| 596 | return err; | ||
| 597 | } | ||
| 598 | cookie = tx->tx_submit(tx); | ||
| 599 | if (dma_submit_error(cookie)) { | ||
| 600 | err = (int)cookie; | ||
| 601 | dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", | ||
| 602 | __func__, __LINE__, err); | ||
| 603 | return err; | ||
| 604 | } | ||
| 605 | dma_async_issue_pending(chan); | ||
| 606 | tx = ddev->device_prep_dma_interrupt(chan, DMA_PREP_INTERRUPT); | ||
| 607 | if (!tx) { | ||
| 608 | err = -ENOMEM; | ||
| 609 | dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", | ||
| 610 | __func__, __LINE__, err); | ||
| 611 | return err; | ||
| 612 | } | ||
| 613 | tx->callback = scif_fence_mark_cb; | ||
| 614 | tx->callback_param = ep; | ||
| 615 | *mark = cookie = tx->tx_submit(tx); | ||
| 616 | if (dma_submit_error(cookie)) { | ||
| 617 | err = (int)cookie; | ||
| 618 | dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", | ||
| 619 | __func__, __LINE__, err); | ||
| 620 | return err; | ||
| 621 | } | ||
| 622 | atomic_inc(&ep->rma_info.fence_refcount); | ||
| 623 | dma_async_issue_pending(chan); | ||
| 624 | return 0; | ||
| 625 | } | ||
| 626 | |||
| 627 | #define SCIF_LOOPB_MAGIC_MARK 0xdead | ||
| 628 | |||
| 629 | int scif_fence_mark(scif_epd_t epd, int flags, int *mark) | ||
| 630 | { | ||
| 631 | struct scif_endpt *ep = (struct scif_endpt *)epd; | ||
| 632 | int err = 0; | ||
| 633 | |||
| 634 | dev_dbg(scif_info.mdev.this_device, | ||
| 635 | "SCIFAPI fence_mark: ep %p flags 0x%x mark 0x%x\n", | ||
| 636 | ep, flags, *mark); | ||
| 637 | err = scif_verify_epd(ep); | ||
| 638 | if (err) | ||
| 639 | return err; | ||
| 640 | |||
| 641 | /* Invalid flags? */ | ||
| 642 | if (flags & ~(SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER)) | ||
| 643 | return -EINVAL; | ||
| 644 | |||
| 645 | /* At least one of init self or peer RMA should be set */ | ||
| 646 | if (!(flags & (SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER))) | ||
| 647 | return -EINVAL; | ||
| 648 | |||
| 649 | /* Exactly one of init self or peer RMA should be set but not both */ | ||
| 650 | if ((flags & SCIF_FENCE_INIT_SELF) && (flags & SCIF_FENCE_INIT_PEER)) | ||
| 651 | return -EINVAL; | ||
| 652 | |||
| 653 | /* | ||
| 654 | * Management node loopback does not need to use DMA. | ||
| 655 | * Return a valid mark to be symmetric. | ||
| 656 | */ | ||
| 657 | if (scifdev_self(ep->remote_dev) && scif_is_mgmt_node()) { | ||
| 658 | *mark = SCIF_LOOPB_MAGIC_MARK; | ||
| 659 | return 0; | ||
| 660 | } | ||
| 661 | |||
| 662 | if (flags & SCIF_FENCE_INIT_SELF) | ||
| 663 | err = _scif_fence_mark(epd, mark); | ||
| 664 | else | ||
| 665 | err = scif_send_fence_mark(ep, mark); | ||
| 666 | |||
| 667 | if (err) | ||
| 668 | dev_err(scif_info.mdev.this_device, | ||
| 669 | "%s %d err %d\n", __func__, __LINE__, err); | ||
| 670 | dev_dbg(scif_info.mdev.this_device, | ||
| 671 | "SCIFAPI fence_mark: ep %p flags 0x%x mark 0x%x err %d\n", | ||
| 672 | ep, flags, *mark, err); | ||
| 673 | return err; | ||
| 674 | } | ||
| 675 | EXPORT_SYMBOL_GPL(scif_fence_mark); | ||
| 676 | |||
| 677 | int scif_fence_wait(scif_epd_t epd, int mark) | ||
| 678 | { | ||
| 679 | struct scif_endpt *ep = (struct scif_endpt *)epd; | ||
| 680 | int err = 0; | ||
| 681 | |||
| 682 | dev_dbg(scif_info.mdev.this_device, | ||
| 683 | "SCIFAPI fence_wait: ep %p mark 0x%x\n", | ||
| 684 | ep, mark); | ||
| 685 | err = scif_verify_epd(ep); | ||
| 686 | if (err) | ||
| 687 | return err; | ||
| 688 | /* | ||
| 689 | * Management node loopback does not need to use DMA. | ||
| 690 | * The only valid mark provided is 0 so simply | ||
| 691 | * return success if the mark is valid. | ||
| 692 | */ | ||
| 693 | if (scifdev_self(ep->remote_dev) && scif_is_mgmt_node()) { | ||
| 694 | if (mark == SCIF_LOOPB_MAGIC_MARK) | ||
| 695 | return 0; | ||
| 696 | else | ||
| 697 | return -EINVAL; | ||
| 698 | } | ||
| 699 | if (mark & SCIF_REMOTE_FENCE) | ||
| 700 | err = scif_send_fence_wait(epd, mark); | ||
| 701 | else | ||
| 702 | err = _scif_fence_wait(epd, mark); | ||
| 703 | if (err < 0) | ||
| 704 | dev_err(scif_info.mdev.this_device, | ||
| 705 | "%s %d err %d\n", __func__, __LINE__, err); | ||
| 706 | return err; | ||
| 707 | } | ||
| 708 | EXPORT_SYMBOL_GPL(scif_fence_wait); | ||
| 709 | |||
| 710 | int scif_fence_signal(scif_epd_t epd, off_t loff, u64 lval, | ||
| 711 | off_t roff, u64 rval, int flags) | ||
| 712 | { | ||
| 713 | struct scif_endpt *ep = (struct scif_endpt *)epd; | ||
| 714 | int err = 0; | ||
| 715 | |||
| 716 | dev_dbg(scif_info.mdev.this_device, | ||
| 717 | "SCIFAPI fence_signal: ep %p loff 0x%lx lval 0x%llx roff 0x%lx rval 0x%llx flags 0x%x\n", | ||
| 718 | ep, loff, lval, roff, rval, flags); | ||
| 719 | err = scif_verify_epd(ep); | ||
| 720 | if (err) | ||
| 721 | return err; | ||
| 722 | |||
| 723 | /* Invalid flags? */ | ||
| 724 | if (flags & ~(SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER | | ||
| 725 | SCIF_SIGNAL_LOCAL | SCIF_SIGNAL_REMOTE)) | ||
| 726 | return -EINVAL; | ||
| 727 | |||
| 728 | /* At least one of init self or peer RMA should be set */ | ||
| 729 | if (!(flags & (SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER))) | ||
| 730 | return -EINVAL; | ||
| 731 | |||
| 732 | /* Exactly one of init self or peer RMA should be set but not both */ | ||
| 733 | if ((flags & SCIF_FENCE_INIT_SELF) && (flags & SCIF_FENCE_INIT_PEER)) | ||
| 734 | return -EINVAL; | ||
| 735 | |||
| 736 | /* At least one of SCIF_SIGNAL_LOCAL or SCIF_SIGNAL_REMOTE required */ | ||
| 737 | if (!(flags & (SCIF_SIGNAL_LOCAL | SCIF_SIGNAL_REMOTE))) | ||
| 738 | return -EINVAL; | ||
| 739 | |||
| 740 | /* Only Dword offsets allowed */ | ||
| 741 | if ((flags & SCIF_SIGNAL_LOCAL) && (loff & (sizeof(u32) - 1))) | ||
| 742 | return -EINVAL; | ||
| 743 | |||
| 744 | /* Only Dword aligned offsets allowed */ | ||
| 745 | if ((flags & SCIF_SIGNAL_REMOTE) && (roff & (sizeof(u32) - 1))) | ||
| 746 | return -EINVAL; | ||
| 747 | |||
| 748 | if (flags & SCIF_FENCE_INIT_PEER) { | ||
| 749 | err = scif_send_fence_signal(epd, roff, rval, loff, | ||
| 750 | lval, flags); | ||
| 751 | } else { | ||
| 752 | /* Local Signal in Local RAS */ | ||
| 753 | if (flags & SCIF_SIGNAL_LOCAL) { | ||
| 754 | err = scif_prog_signal(epd, loff, lval, | ||
| 755 | SCIF_WINDOW_SELF); | ||
| 756 | if (err) | ||
| 757 | goto error_ret; | ||
| 758 | } | ||
| 759 | |||
| 760 | /* Signal in Remote RAS */ | ||
| 761 | if (flags & SCIF_SIGNAL_REMOTE) | ||
| 762 | err = scif_prog_signal(epd, roff, | ||
| 763 | rval, SCIF_WINDOW_PEER); | ||
| 764 | } | ||
| 765 | error_ret: | ||
| 766 | if (err) | ||
| 767 | dev_err(scif_info.mdev.this_device, | ||
| 768 | "%s %d err %d\n", __func__, __LINE__, err); | ||
| 769 | return err; | ||
| 770 | } | ||
| 771 | EXPORT_SYMBOL_GPL(scif_fence_signal); | ||
