rdma.c 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. #include <linux/version.h>
  2. #include <linux/string.h>
  3. #include <linux/types.h>
  4. #include <linux/list.h>
  5. #include <linux/pci.h>
  6. #include <linux/wait.h>
  7. #include <linux/mm.h>
  8. #include <linux/pagemap.h>
  9. #include <linux/hugetlb.h>
  10. #include <linux/cdev.h>
  11. #include <linux/version.h>
  12. #include "base.h"
  13. static unsigned long pcidriver_follow_pte(struct mm_struct *mm, unsigned long address)
  14. {
  15. pgd_t *pgd;
  16. pud_t *pud;
  17. pmd_t *pmd;
  18. pte_t *pte;
  19. spinlock_t *ptl;
  20. unsigned long pfn = 0;
  21. pgd = pgd_offset(mm, address);
  22. if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
  23. return 0;
  24. // pud_offset compatibility with pgd_t* broken from Kernel Version 4.12 onwards. See: https://github.com/torvalds/linux/commit/048456dcf2c56ad6f6248e2899dda92fb6a613f6
  25. #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,12,0)
  26. p4d_t *p4d;
  27. p4d = p4d_offset(pgd, address);
  28. pud = pud_offset(p4d, address);
  29. #else
  30. pud = pud_offset(pgd, address);
  31. #endif
  32. if (pud_none(*pud) || unlikely(pud_bad(*pud)))
  33. return 0;
  34. pmd = pmd_offset(pud, address);
  35. if (pmd_none(*pmd))
  36. return 0;
  37. pte = pte_offset_map_lock(mm, pmd, address, &ptl);
  38. if (!pte_none(*pte))
  39. pfn = (pte_pfn(*pte) << PAGE_SHIFT);
  40. pte_unmap_unlock(pte, ptl);
  41. return pfn;
  42. }
  43. unsigned long pcidriver_resolve_bar(unsigned long bar_address) {
  44. int dev, bar;
  45. unsigned long pfn;
  46. unsigned long address;
  47. unsigned long offset;
  48. address = (bar_address >> PAGE_SHIFT) << PAGE_SHIFT;
  49. offset = bar_address - address;
  50. pfn = pcidriver_follow_pte(current->mm, address);
  51. for (dev = 0; dev < MAXDEVICES; dev++)
  52. {
  53. pcidriver_privdata_t *privdata = pcidriver_get_privdata(dev);
  54. if (!privdata) continue;
  55. for (bar = 0; bar < 6; bar++)
  56. {
  57. unsigned long start = pci_resource_start(privdata->pdev, bar);
  58. unsigned long end = start + pci_resource_len(privdata->pdev, bar);
  59. if ((pfn >= start)&&(pfn < end))
  60. return pfn + offset;
  61. }
  62. pcidriver_put_privdata(privdata);
  63. }
  64. return 0;
  65. }
  66. EXPORT_SYMBOL(pcidriver_resolve_bar);