| .. | .. |
|---|
| 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); |
|---|