Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

accel/habanalabs: add debugfs interface for HLDIO testing

Add debugfs files for NVMe Direct I/O (HLDIO) functionality.
This interface allows userspace access to direct SSD ↔ device transfers
through debugfs nodes.

Four debugfs files are created under /sys/kernel/debug/habanalabs/hlN/:

- dio_ssd2hl : trigger SSD-to-device transfers
- dio_hl2ssd : trigger device-to-SSD transfers
(placeholder, not yet implemented)
- dio_stats : show transfer statistics
- dio_reset : reset statistics counters

Usage examples:

# Perform SSD → device transfer
echo "fd=3 va=0x10000 off=0 len=4096" > \
/sys/kernel/debug/habanalabs/hl0/dio_ssd2hl

# View statistics
cat /sys/kernel/debug/habanalabs/hl0/dio_stats

# Reset counters
echo 1 > /sys/kernel/debug/habanalabs/hl0/dio_reset

This interface provides access to HLDIO functionality for validation
and diagnostics.

Signed-off-by: Konstantin Sinyuk <konstantin.sinyuk@intel.com>
Reviewed-by: Farah Kassabri <farah.kassabri@intel.com>
Reviewed-by: Koby Elbaz <koby.elbaz@intel.com>
Signed-off-by: Koby Elbaz <koby.elbaz@intel.com>

authored by

Konstantin Sinyuk and committed by
Koby Elbaz
eeb38d0e 8cbacc9a

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