.. | .. |
---|
6 | 6 | #include <linux/mmu_context.h> |
---|
7 | 7 | #include <asm/copro.h> |
---|
8 | 8 | #include <asm/pnv-ocxl.h> |
---|
| 9 | +#include <asm/xive.h> |
---|
9 | 10 | #include <misc/ocxl.h> |
---|
10 | 11 | #include "ocxl_internal.h" |
---|
11 | 12 | #include "trace.h" |
---|
.. | .. |
---|
76 | 77 | * limited number of opencapi slots on a system and lookup is only |
---|
77 | 78 | * done when the device is probed |
---|
78 | 79 | */ |
---|
79 | | -struct link { |
---|
| 80 | +struct ocxl_link { |
---|
80 | 81 | struct list_head list; |
---|
81 | 82 | struct kref ref; |
---|
82 | 83 | int domain; |
---|
.. | .. |
---|
163 | 164 | if (fault->dsisr & SPA_XSL_S) |
---|
164 | 165 | access |= _PAGE_WRITE; |
---|
165 | 166 | |
---|
166 | | - if (REGION_ID(fault->dar) != USER_REGION_ID) |
---|
| 167 | + if (get_region_id(fault->dar) != USER_REGION_ID) |
---|
167 | 168 | access |= _PAGE_PRIVILEGED; |
---|
168 | 169 | |
---|
169 | 170 | local_irq_save(flags); |
---|
.. | .. |
---|
179 | 180 | |
---|
180 | 181 | static irqreturn_t xsl_fault_handler(int irq, void *data) |
---|
181 | 182 | { |
---|
182 | | - struct link *link = (struct link *) data; |
---|
| 183 | + struct ocxl_link *link = (struct ocxl_link *) data; |
---|
183 | 184 | struct spa *spa = link->spa; |
---|
184 | 185 | u64 dsisr, dar, pe_handle; |
---|
185 | 186 | struct pe_data *pe_data; |
---|
186 | 187 | struct ocxl_process_element *pe; |
---|
187 | | - int lpid, pid, tid; |
---|
| 188 | + int pid; |
---|
188 | 189 | bool schedule = false; |
---|
189 | 190 | |
---|
190 | 191 | read_irq(spa, &dsisr, &dar, &pe_handle); |
---|
.. | .. |
---|
192 | 193 | |
---|
193 | 194 | WARN_ON(pe_handle > SPA_PE_MASK); |
---|
194 | 195 | pe = spa->spa_mem + pe_handle; |
---|
195 | | - lpid = be32_to_cpu(pe->lpid); |
---|
196 | 196 | pid = be32_to_cpu(pe->pid); |
---|
197 | | - tid = be32_to_cpu(pe->tid); |
---|
198 | 197 | /* We could be reading all null values here if the PE is being |
---|
199 | 198 | * removed while an interrupt kicks in. It's not supposed to |
---|
200 | 199 | * happen if the driver notified the AFU to terminate the |
---|
.. | .. |
---|
223 | 222 | */ |
---|
224 | 223 | rcu_read_unlock(); |
---|
225 | 224 | pr_debug("Unknown mm context for xsl interrupt\n"); |
---|
| 225 | + ack_irq(spa, ADDRESS_ERROR); |
---|
| 226 | + return IRQ_HANDLED; |
---|
| 227 | + } |
---|
| 228 | + |
---|
| 229 | + if (!pe_data->mm) { |
---|
| 230 | + /* |
---|
| 231 | + * translation fault from a kernel context - an OpenCAPI |
---|
| 232 | + * device tried to access a bad kernel address |
---|
| 233 | + */ |
---|
| 234 | + rcu_read_unlock(); |
---|
| 235 | + pr_warn("Unresolved OpenCAPI xsl fault in kernel context\n"); |
---|
226 | 236 | ack_irq(spa, ADDRESS_ERROR); |
---|
227 | 237 | return IRQ_HANDLED; |
---|
228 | 238 | } |
---|
.. | .. |
---|
256 | 266 | &spa->reg_tfc, &spa->reg_pe_handle); |
---|
257 | 267 | } |
---|
258 | 268 | |
---|
259 | | -static int setup_xsl_irq(struct pci_dev *dev, struct link *link) |
---|
| 269 | +static int setup_xsl_irq(struct pci_dev *dev, struct ocxl_link *link) |
---|
260 | 270 | { |
---|
261 | 271 | struct spa *spa = link->spa; |
---|
262 | 272 | int rc; |
---|
.. | .. |
---|
273 | 283 | spa->irq_name = kasprintf(GFP_KERNEL, "ocxl-xsl-%x-%x-%x", |
---|
274 | 284 | link->domain, link->bus, link->dev); |
---|
275 | 285 | if (!spa->irq_name) { |
---|
276 | | - unmap_irq_registers(spa); |
---|
277 | 286 | dev_err(&dev->dev, "Can't allocate name for xsl interrupt\n"); |
---|
278 | | - return -ENOMEM; |
---|
| 287 | + rc = -ENOMEM; |
---|
| 288 | + goto err_xsl; |
---|
279 | 289 | } |
---|
280 | 290 | /* |
---|
281 | 291 | * At some point, we'll need to look into allowing a higher |
---|
.. | .. |
---|
283 | 293 | */ |
---|
284 | 294 | spa->virq = irq_create_mapping(NULL, hwirq); |
---|
285 | 295 | if (!spa->virq) { |
---|
286 | | - kfree(spa->irq_name); |
---|
287 | | - unmap_irq_registers(spa); |
---|
288 | 296 | dev_err(&dev->dev, |
---|
289 | 297 | "irq_create_mapping failed for translation interrupt\n"); |
---|
290 | | - return -EINVAL; |
---|
| 298 | + rc = -EINVAL; |
---|
| 299 | + goto err_name; |
---|
291 | 300 | } |
---|
292 | 301 | |
---|
293 | 302 | dev_dbg(&dev->dev, "hwirq %d mapped to virq %d\n", hwirq, spa->virq); |
---|
.. | .. |
---|
295 | 304 | rc = request_irq(spa->virq, xsl_fault_handler, 0, spa->irq_name, |
---|
296 | 305 | link); |
---|
297 | 306 | if (rc) { |
---|
298 | | - irq_dispose_mapping(spa->virq); |
---|
299 | | - kfree(spa->irq_name); |
---|
300 | | - unmap_irq_registers(spa); |
---|
301 | 307 | dev_err(&dev->dev, |
---|
302 | 308 | "request_irq failed for translation interrupt: %d\n", |
---|
303 | 309 | rc); |
---|
304 | | - return -EINVAL; |
---|
| 310 | + rc = -EINVAL; |
---|
| 311 | + goto err_mapping; |
---|
305 | 312 | } |
---|
306 | 313 | return 0; |
---|
| 314 | + |
---|
| 315 | +err_mapping: |
---|
| 316 | + irq_dispose_mapping(spa->virq); |
---|
| 317 | +err_name: |
---|
| 318 | + kfree(spa->irq_name); |
---|
| 319 | +err_xsl: |
---|
| 320 | + unmap_irq_registers(spa); |
---|
| 321 | + return rc; |
---|
307 | 322 | } |
---|
308 | 323 | |
---|
309 | | -static void release_xsl_irq(struct link *link) |
---|
| 324 | +static void release_xsl_irq(struct ocxl_link *link) |
---|
310 | 325 | { |
---|
311 | 326 | struct spa *spa = link->spa; |
---|
312 | 327 | |
---|
.. | .. |
---|
318 | 333 | unmap_irq_registers(spa); |
---|
319 | 334 | } |
---|
320 | 335 | |
---|
321 | | -static int alloc_spa(struct pci_dev *dev, struct link *link) |
---|
| 336 | +static int alloc_spa(struct pci_dev *dev, struct ocxl_link *link) |
---|
322 | 337 | { |
---|
323 | 338 | struct spa *spa; |
---|
324 | 339 | |
---|
.. | .. |
---|
345 | 360 | return 0; |
---|
346 | 361 | } |
---|
347 | 362 | |
---|
348 | | -static void free_spa(struct link *link) |
---|
| 363 | +static void free_spa(struct ocxl_link *link) |
---|
349 | 364 | { |
---|
350 | 365 | struct spa *spa = link->spa; |
---|
351 | 366 | |
---|
.. | .. |
---|
359 | 374 | } |
---|
360 | 375 | } |
---|
361 | 376 | |
---|
362 | | -static int alloc_link(struct pci_dev *dev, int PE_mask, struct link **out_link) |
---|
| 377 | +static int alloc_link(struct pci_dev *dev, int PE_mask, struct ocxl_link **out_link) |
---|
363 | 378 | { |
---|
364 | | - struct link *link; |
---|
| 379 | + struct ocxl_link *link; |
---|
365 | 380 | int rc; |
---|
366 | 381 | |
---|
367 | | - link = kzalloc(sizeof(struct link), GFP_KERNEL); |
---|
| 382 | + link = kzalloc(sizeof(struct ocxl_link), GFP_KERNEL); |
---|
368 | 383 | if (!link) |
---|
369 | 384 | return -ENOMEM; |
---|
370 | 385 | |
---|
.. | .. |
---|
400 | 415 | return rc; |
---|
401 | 416 | } |
---|
402 | 417 | |
---|
403 | | -static void free_link(struct link *link) |
---|
| 418 | +static void free_link(struct ocxl_link *link) |
---|
404 | 419 | { |
---|
405 | 420 | release_xsl_irq(link); |
---|
406 | 421 | free_spa(link); |
---|
.. | .. |
---|
410 | 425 | int ocxl_link_setup(struct pci_dev *dev, int PE_mask, void **link_handle) |
---|
411 | 426 | { |
---|
412 | 427 | int rc = 0; |
---|
413 | | - struct link *link; |
---|
| 428 | + struct ocxl_link *link; |
---|
414 | 429 | |
---|
415 | 430 | mutex_lock(&links_list_lock); |
---|
416 | 431 | list_for_each_entry(link, &links_list, list) { |
---|
.. | .. |
---|
437 | 452 | |
---|
438 | 453 | static void release_xsl(struct kref *ref) |
---|
439 | 454 | { |
---|
440 | | - struct link *link = container_of(ref, struct link, ref); |
---|
| 455 | + struct ocxl_link *link = container_of(ref, struct ocxl_link, ref); |
---|
441 | 456 | |
---|
442 | 457 | list_del(&link->list); |
---|
443 | 458 | /* call platform code before releasing data */ |
---|
.. | .. |
---|
447 | 462 | |
---|
448 | 463 | void ocxl_link_release(struct pci_dev *dev, void *link_handle) |
---|
449 | 464 | { |
---|
450 | | - struct link *link = (struct link *) link_handle; |
---|
| 465 | + struct ocxl_link *link = (struct ocxl_link *) link_handle; |
---|
451 | 466 | |
---|
452 | 467 | mutex_lock(&links_list_lock); |
---|
453 | 468 | kref_put(&link->ref, release_xsl); |
---|
.. | .. |
---|
483 | 498 | void (*xsl_err_cb)(void *data, u64 addr, u64 dsisr), |
---|
484 | 499 | void *xsl_err_data) |
---|
485 | 500 | { |
---|
486 | | - struct link *link = (struct link *) link_handle; |
---|
| 501 | + struct ocxl_link *link = (struct ocxl_link *) link_handle; |
---|
487 | 502 | struct spa *spa = link->spa; |
---|
488 | 503 | struct ocxl_process_element *pe; |
---|
489 | 504 | int pe_handle, rc = 0; |
---|
.. | .. |
---|
520 | 535 | pe->amr = cpu_to_be64(amr); |
---|
521 | 536 | pe->software_state = cpu_to_be32(SPA_PE_VALID); |
---|
522 | 537 | |
---|
523 | | - mm_context_add_copro(mm); |
---|
| 538 | + /* |
---|
| 539 | + * For user contexts, register a copro so that TLBIs are seen |
---|
| 540 | + * by the nest MMU. If we have a kernel context, TLBIs are |
---|
| 541 | + * already global. |
---|
| 542 | + */ |
---|
| 543 | + if (mm) |
---|
| 544 | + mm_context_add_copro(mm); |
---|
524 | 545 | /* |
---|
525 | 546 | * Barrier is to make sure PE is visible in the SPA before it |
---|
526 | 547 | * is used by the device. It also helps with the global TLBI |
---|
.. | .. |
---|
543 | 564 | * have a reference on mm_users. Incrementing mm_count solves |
---|
544 | 565 | * the problem. |
---|
545 | 566 | */ |
---|
546 | | - mmgrab(mm); |
---|
| 567 | + if (mm) |
---|
| 568 | + mmgrab(mm); |
---|
547 | 569 | trace_ocxl_context_add(current->pid, spa->spa_mem, pasid, pidr, tidr); |
---|
548 | 570 | unlock: |
---|
549 | 571 | mutex_unlock(&spa->spa_lock); |
---|
.. | .. |
---|
553 | 575 | |
---|
554 | 576 | int ocxl_link_update_pe(void *link_handle, int pasid, __u16 tid) |
---|
555 | 577 | { |
---|
556 | | - struct link *link = (struct link *) link_handle; |
---|
| 578 | + struct ocxl_link *link = (struct ocxl_link *) link_handle; |
---|
557 | 579 | struct spa *spa = link->spa; |
---|
558 | 580 | struct ocxl_process_element *pe; |
---|
559 | 581 | int pe_handle, rc; |
---|
.. | .. |
---|
589 | 611 | |
---|
590 | 612 | int ocxl_link_remove_pe(void *link_handle, int pasid) |
---|
591 | 613 | { |
---|
592 | | - struct link *link = (struct link *) link_handle; |
---|
| 614 | + struct ocxl_link *link = (struct ocxl_link *) link_handle; |
---|
593 | 615 | struct spa *spa = link->spa; |
---|
594 | 616 | struct ocxl_process_element *pe; |
---|
595 | 617 | struct pe_data *pe_data; |
---|
.. | .. |
---|
649 | 671 | if (!pe_data) { |
---|
650 | 672 | WARN(1, "Couldn't find pe data when removing PE\n"); |
---|
651 | 673 | } else { |
---|
652 | | - mm_context_remove_copro(pe_data->mm); |
---|
653 | | - mmdrop(pe_data->mm); |
---|
| 674 | + if (pe_data->mm) { |
---|
| 675 | + mm_context_remove_copro(pe_data->mm); |
---|
| 676 | + mmdrop(pe_data->mm); |
---|
| 677 | + } |
---|
654 | 678 | kfree_rcu(pe_data, rcu); |
---|
655 | 679 | } |
---|
656 | 680 | unlock: |
---|
.. | .. |
---|
659 | 683 | } |
---|
660 | 684 | EXPORT_SYMBOL_GPL(ocxl_link_remove_pe); |
---|
661 | 685 | |
---|
662 | | -int ocxl_link_irq_alloc(void *link_handle, int *hw_irq, u64 *trigger_addr) |
---|
| 686 | +int ocxl_link_irq_alloc(void *link_handle, int *hw_irq) |
---|
663 | 687 | { |
---|
664 | | - struct link *link = (struct link *) link_handle; |
---|
665 | | - int rc, irq; |
---|
666 | | - u64 addr; |
---|
| 688 | + struct ocxl_link *link = (struct ocxl_link *) link_handle; |
---|
| 689 | + int irq; |
---|
667 | 690 | |
---|
668 | 691 | if (atomic_dec_if_positive(&link->irq_available) < 0) |
---|
669 | 692 | return -ENOSPC; |
---|
670 | 693 | |
---|
671 | | - rc = pnv_ocxl_alloc_xive_irq(&irq, &addr); |
---|
672 | | - if (rc) { |
---|
| 694 | + irq = xive_native_alloc_irq(); |
---|
| 695 | + if (!irq) { |
---|
673 | 696 | atomic_inc(&link->irq_available); |
---|
674 | | - return rc; |
---|
| 697 | + return -ENXIO; |
---|
675 | 698 | } |
---|
676 | 699 | |
---|
677 | 700 | *hw_irq = irq; |
---|
678 | | - *trigger_addr = addr; |
---|
679 | 701 | return 0; |
---|
680 | 702 | } |
---|
681 | 703 | EXPORT_SYMBOL_GPL(ocxl_link_irq_alloc); |
---|
682 | 704 | |
---|
683 | 705 | void ocxl_link_free_irq(void *link_handle, int hw_irq) |
---|
684 | 706 | { |
---|
685 | | - struct link *link = (struct link *) link_handle; |
---|
| 707 | + struct ocxl_link *link = (struct ocxl_link *) link_handle; |
---|
686 | 708 | |
---|
687 | | - pnv_ocxl_free_xive_irq(hw_irq); |
---|
| 709 | + xive_native_free_irq(hw_irq); |
---|
688 | 710 | atomic_inc(&link->irq_available); |
---|
689 | 711 | } |
---|
690 | 712 | EXPORT_SYMBOL_GPL(ocxl_link_free_irq); |
---|