|
6 | 6 | */ |
7 | 7 |
|
8 | 8 | #include "habanalabs.h" |
| 9 | +#include "hldio.h" |
9 | 10 | #include "../include/hw_ip/mmu/mmu_general.h" |
10 | 11 |
|
11 | 12 | #include <linux/pci.h> |
@@ -602,6 +603,198 @@ static int engines_show(struct seq_file *s, void *data) |
602 | 603 | return 0; |
603 | 604 | } |
604 | 605 |
|
| 606 | +#ifdef CONFIG_HL_HLDIO |
| 607 | +/* DIO debugfs functions following the standard pattern */ |
| 608 | +static int dio_ssd2hl_show(struct seq_file *s, void *data) |
| 609 | +{ |
| 610 | + struct hl_debugfs_entry *entry = s->private; |
| 611 | + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; |
| 612 | + struct hl_device *hdev = dev_entry->hdev; |
| 613 | + |
| 614 | + if (!hdev->asic_prop.supports_nvme) { |
| 615 | + seq_puts(s, "NVMe Direct I/O not supported\\n"); |
| 616 | + return 0; |
| 617 | + } |
| 618 | + |
| 619 | + seq_puts(s, "Usage: echo \"fd=N va=0xADDR off=N len=N\" > dio_ssd2hl\n"); |
| 620 | + seq_printf(s, "Last transfer: %zu bytes\\n", dev_entry->dio_stats.last_len_read); |
| 621 | + seq_puts(s, "Note: All parameters must be page-aligned (4KB)\\n"); |
| 622 | + |
| 623 | + return 0; |
| 624 | +} |
| 625 | + |
| 626 | +static ssize_t dio_ssd2hl_write(struct file *file, const char __user *buf, |
| 627 | + size_t count, loff_t *f_pos) |
| 628 | +{ |
| 629 | + struct seq_file *s = file->private_data; |
| 630 | + struct hl_debugfs_entry *entry = s->private; |
| 631 | + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; |
| 632 | + struct hl_device *hdev = dev_entry->hdev; |
| 633 | + struct hl_ctx *ctx = hdev->kernel_ctx; |
| 634 | + char kbuf[128]; |
| 635 | + u64 device_va = 0, off_bytes = 0, len_bytes = 0; |
| 636 | + u32 fd = 0; |
| 637 | + size_t len_read = 0; |
| 638 | + int rc, parsed; |
| 639 | + |
| 640 | + if (!hdev->asic_prop.supports_nvme) |
| 641 | + return -EOPNOTSUPP; |
| 642 | + |
| 643 | + if (count >= sizeof(kbuf)) |
| 644 | + return -EINVAL; |
| 645 | + |
| 646 | + if (copy_from_user(kbuf, buf, count)) |
| 647 | + return -EFAULT; |
| 648 | + |
| 649 | + kbuf[count] = 0; |
| 650 | + |
| 651 | + /* Parse: fd=N va=0xADDR off=N len=N */ |
| 652 | + parsed = sscanf(kbuf, "fd=%u va=0x%llx off=%llu len=%llu", |
| 653 | + &fd, &device_va, &off_bytes, &len_bytes); |
| 654 | + if (parsed != 4) { |
| 655 | + dev_err(hdev->dev, "Invalid format. Expected: fd=N va=0xADDR off=N len=N\\n"); |
| 656 | + return -EINVAL; |
| 657 | + } |
| 658 | + |
| 659 | + /* Validate file descriptor */ |
| 660 | + if (fd == 0) { |
| 661 | + dev_err(hdev->dev, "Invalid file descriptor: %u\\n", fd); |
| 662 | + return -EINVAL; |
| 663 | + } |
| 664 | + |
| 665 | + /* Validate alignment requirements */ |
| 666 | + if (!IS_ALIGNED(device_va, PAGE_SIZE) || |
| 667 | + !IS_ALIGNED(off_bytes, PAGE_SIZE) || |
| 668 | + !IS_ALIGNED(len_bytes, PAGE_SIZE)) { |
| 669 | + dev_err(hdev->dev, |
| 670 | + "All parameters must be page-aligned (4KB)\\n"); |
| 671 | + return -EINVAL; |
| 672 | + } |
| 673 | + |
| 674 | + /* Validate transfer size */ |
| 675 | + if (len_bytes == 0 || len_bytes > SZ_1G) { |
| 676 | + dev_err(hdev->dev, "Invalid length: %llu (max 1GB)\\n", |
| 677 | + len_bytes); |
| 678 | + return -EINVAL; |
| 679 | + } |
| 680 | + |
| 681 | + dev_dbg(hdev->dev, "DIO SSD2HL: fd=%u va=0x%llx off=%llu len=%llu\\n", |
| 682 | + fd, device_va, off_bytes, len_bytes); |
| 683 | + |
| 684 | + rc = hl_dio_ssd2hl(hdev, ctx, fd, device_va, off_bytes, len_bytes, &len_read); |
| 685 | + if (rc < 0) { |
| 686 | + dev_entry->dio_stats.failed_ops++; |
| 687 | + dev_err(hdev->dev, "SSD2HL operation failed: %d\\n", rc); |
| 688 | + return rc; |
| 689 | + } |
| 690 | + |
| 691 | + /* Update statistics */ |
| 692 | + dev_entry->dio_stats.total_ops++; |
| 693 | + dev_entry->dio_stats.successful_ops++; |
| 694 | + dev_entry->dio_stats.bytes_transferred += len_read; |
| 695 | + dev_entry->dio_stats.last_len_read = len_read; |
| 696 | + |
| 697 | + dev_dbg(hdev->dev, "DIO SSD2HL completed: %zu bytes transferred\\n", len_read); |
| 698 | + |
| 699 | + return count; |
| 700 | +} |
| 701 | + |
| 702 | +static int dio_hl2ssd_show(struct seq_file *s, void *data) |
| 703 | +{ |
| 704 | + seq_puts(s, "HL2SSD (device-to-SSD) transfers not implemented\\n"); |
| 705 | + return 0; |
| 706 | +} |
| 707 | + |
| 708 | +static ssize_t dio_hl2ssd_write(struct file *file, const char __user *buf, |
| 709 | + size_t count, loff_t *f_pos) |
| 710 | +{ |
| 711 | + struct seq_file *s = file->private_data; |
| 712 | + struct hl_debugfs_entry *entry = s->private; |
| 713 | + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; |
| 714 | + struct hl_device *hdev = dev_entry->hdev; |
| 715 | + |
| 716 | + if (!hdev->asic_prop.supports_nvme) |
| 717 | + return -EOPNOTSUPP; |
| 718 | + |
| 719 | + dev_dbg(hdev->dev, "HL2SSD operation not implemented\\n"); |
| 720 | + return -EOPNOTSUPP; |
| 721 | +} |
| 722 | + |
| 723 | +static int dio_stats_show(struct seq_file *s, void *data) |
| 724 | +{ |
| 725 | + struct hl_debugfs_entry *entry = s->private; |
| 726 | + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; |
| 727 | + struct hl_device *hdev = dev_entry->hdev; |
| 728 | + struct hl_dio_stats *stats = &dev_entry->dio_stats; |
| 729 | + u64 avg_bytes_per_op = 0, success_rate = 0; |
| 730 | + |
| 731 | + if (!hdev->asic_prop.supports_nvme) { |
| 732 | + seq_puts(s, "NVMe Direct I/O not supported\\n"); |
| 733 | + return 0; |
| 734 | + } |
| 735 | + |
| 736 | + if (stats->successful_ops > 0) |
| 737 | + avg_bytes_per_op = stats->bytes_transferred / stats->successful_ops; |
| 738 | + |
| 739 | + if (stats->total_ops > 0) |
| 740 | + success_rate = (stats->successful_ops * 100) / stats->total_ops; |
| 741 | + |
| 742 | + seq_puts(s, "=== Habanalabs Direct I/O Statistics ===\\n"); |
| 743 | + seq_printf(s, "Total operations: %llu\\n", stats->total_ops); |
| 744 | + seq_printf(s, "Successful ops: %llu\\n", stats->successful_ops); |
| 745 | + seq_printf(s, "Failed ops: %llu\\n", stats->failed_ops); |
| 746 | + seq_printf(s, "Success rate: %llu%%\\n", success_rate); |
| 747 | + seq_printf(s, "Total bytes: %llu\\n", stats->bytes_transferred); |
| 748 | + seq_printf(s, "Avg bytes per op: %llu\\n", avg_bytes_per_op); |
| 749 | + seq_printf(s, "Last transfer: %zu bytes\\n", stats->last_len_read); |
| 750 | + |
| 751 | + return 0; |
| 752 | +} |
| 753 | + |
| 754 | +static int dio_reset_show(struct seq_file *s, void *data) |
| 755 | +{ |
| 756 | + seq_puts(s, "Write '1' to reset DIO statistics\\n"); |
| 757 | + return 0; |
| 758 | +} |
| 759 | + |
| 760 | +static ssize_t dio_reset_write(struct file *file, const char __user *buf, |
| 761 | + size_t count, loff_t *f_pos) |
| 762 | +{ |
| 763 | + struct seq_file *s = file->private_data; |
| 764 | + struct hl_debugfs_entry *entry = s->private; |
| 765 | + struct hl_dbg_device_entry *dev_entry = entry->dev_entry; |
| 766 | + struct hl_device *hdev = dev_entry->hdev; |
| 767 | + char kbuf[8]; |
| 768 | + unsigned long val; |
| 769 | + int rc; |
| 770 | + |
| 771 | + if (!hdev->asic_prop.supports_nvme) |
| 772 | + return -EOPNOTSUPP; |
| 773 | + |
| 774 | + if (count >= sizeof(kbuf)) |
| 775 | + return -EINVAL; |
| 776 | + |
| 777 | + if (copy_from_user(kbuf, buf, count)) |
| 778 | + return -EFAULT; |
| 779 | + |
| 780 | + kbuf[count] = 0; |
| 781 | + |
| 782 | + rc = kstrtoul(kbuf, 0, &val); |
| 783 | + if (rc) |
| 784 | + return rc; |
| 785 | + |
| 786 | + if (val == 1) { |
| 787 | + memset(&dev_entry->dio_stats, 0, sizeof(dev_entry->dio_stats)); |
| 788 | + dev_dbg(hdev->dev, "DIO statistics reset\\n"); |
| 789 | + } else { |
| 790 | + dev_err(hdev->dev, "Write '1' to reset statistics\\n"); |
| 791 | + return -EINVAL; |
| 792 | + } |
| 793 | + |
| 794 | + return count; |
| 795 | +} |
| 796 | +#endif |
| 797 | + |
605 | 798 | static ssize_t hl_memory_scrub(struct file *f, const char __user *buf, |
606 | 799 | size_t count, loff_t *ppos) |
607 | 800 | { |
@@ -1633,6 +1826,13 @@ static const struct hl_info_list hl_debugfs_list[] = { |
1633 | 1826 | {"mmu", mmu_show, mmu_asid_va_write}, |
1634 | 1827 | {"mmu_error", mmu_ack_error, mmu_ack_error_value_write}, |
1635 | 1828 | {"engines", engines_show, NULL}, |
| 1829 | +#ifdef CONFIG_HL_HLDIO |
| 1830 | + /* DIO entries - only created if NVMe is supported */ |
| 1831 | + {"dio_ssd2hl", dio_ssd2hl_show, dio_ssd2hl_write}, |
| 1832 | + {"dio_stats", dio_stats_show, NULL}, |
| 1833 | + {"dio_reset", dio_reset_show, dio_reset_write}, |
| 1834 | + {"dio_hl2ssd", dio_hl2ssd_show, dio_hl2ssd_write}, |
| 1835 | +#endif |
1636 | 1836 | }; |
1637 | 1837 |
|
1638 | 1838 | static int hl_debugfs_open(struct inode *inode, struct file *file) |
@@ -1831,6 +2031,11 @@ static void add_files_to_device(struct hl_device *hdev, struct hl_dbg_device_ent |
1831 | 2031 | &hdev->asic_prop.server_type); |
1832 | 2032 |
|
1833 | 2033 | for (i = 0, entry = dev_entry->entry_arr ; i < count ; i++, entry++) { |
| 2034 | + /* Skip DIO entries if NVMe is not supported */ |
| 2035 | + if (strncmp(hl_debugfs_list[i].name, "dio_", 4) == 0 && |
| 2036 | + !hdev->asic_prop.supports_nvme) |
| 2037 | + continue; |
| 2038 | + |
1834 | 2039 | debugfs_create_file(hl_debugfs_list[i].name, |
1835 | 2040 | 0644, |
1836 | 2041 | root, |
@@ -1873,6 +2078,11 @@ int hl_debugfs_device_init(struct hl_device *hdev) |
1873 | 2078 | spin_lock_init(&hdev->debugfs_cfg_accesses.lock); |
1874 | 2079 | hdev->debugfs_cfg_accesses.head = 0; /* already zero by alloc but explicit init is fine */ |
1875 | 2080 |
|
| 2081 | +#ifdef CONFIG_HL_HLDIO |
| 2082 | + /* Initialize DIO statistics */ |
| 2083 | + memset(&dev_entry->dio_stats, 0, sizeof(dev_entry->dio_stats)); |
| 2084 | +#endif |
| 2085 | + |
1876 | 2086 | return 0; |
1877 | 2087 | } |
1878 | 2088 |
|
|
0 commit comments