vc4_perfmon.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2018 Broadcom
  4. */
  5. /**
  6. * DOC: VC4 V3D performance monitor module
  7. *
  8. * The V3D block provides 16 hardware counters which can count various events.
  9. */
  10. #include "vc4_drv.h"
  11. #include "vc4_regs.h"
  12. #define VC4_PERFMONID_MIN 1
  13. #define VC4_PERFMONID_MAX U32_MAX
  14. void vc4_perfmon_get(struct vc4_perfmon *perfmon)
  15. {
  16. if (perfmon)
  17. refcount_inc(&perfmon->refcnt);
  18. }
  19. void vc4_perfmon_put(struct vc4_perfmon *perfmon)
  20. {
  21. if (perfmon && refcount_dec_and_test(&perfmon->refcnt))
  22. kfree(perfmon);
  23. }
  24. void vc4_perfmon_start(struct vc4_dev *vc4, struct vc4_perfmon *perfmon)
  25. {
  26. unsigned int i;
  27. u32 mask;
  28. if (WARN_ON_ONCE(!perfmon || vc4->active_perfmon))
  29. return;
  30. for (i = 0; i < perfmon->ncounters; i++)
  31. V3D_WRITE(V3D_PCTRS(i), perfmon->events[i]);
  32. mask = GENMASK(perfmon->ncounters - 1, 0);
  33. V3D_WRITE(V3D_PCTRC, mask);
  34. V3D_WRITE(V3D_PCTRE, V3D_PCTRE_EN | mask);
  35. vc4->active_perfmon = perfmon;
  36. }
  37. void vc4_perfmon_stop(struct vc4_dev *vc4, struct vc4_perfmon *perfmon,
  38. bool capture)
  39. {
  40. unsigned int i;
  41. if (WARN_ON_ONCE(!vc4->active_perfmon ||
  42. perfmon != vc4->active_perfmon))
  43. return;
  44. if (capture) {
  45. for (i = 0; i < perfmon->ncounters; i++)
  46. perfmon->counters[i] += V3D_READ(V3D_PCTR(i));
  47. }
  48. V3D_WRITE(V3D_PCTRE, 0);
  49. vc4->active_perfmon = NULL;
  50. }
  51. struct vc4_perfmon *vc4_perfmon_find(struct vc4_file *vc4file, int id)
  52. {
  53. struct vc4_perfmon *perfmon;
  54. mutex_lock(&vc4file->perfmon.lock);
  55. perfmon = idr_find(&vc4file->perfmon.idr, id);
  56. vc4_perfmon_get(perfmon);
  57. mutex_unlock(&vc4file->perfmon.lock);
  58. return perfmon;
  59. }
  60. void vc4_perfmon_open_file(struct vc4_file *vc4file)
  61. {
  62. mutex_init(&vc4file->perfmon.lock);
  63. idr_init(&vc4file->perfmon.idr);
  64. }
  65. static int vc4_perfmon_idr_del(int id, void *elem, void *data)
  66. {
  67. struct vc4_perfmon *perfmon = elem;
  68. vc4_perfmon_put(perfmon);
  69. return 0;
  70. }
  71. void vc4_perfmon_close_file(struct vc4_file *vc4file)
  72. {
  73. mutex_lock(&vc4file->perfmon.lock);
  74. idr_for_each(&vc4file->perfmon.idr, vc4_perfmon_idr_del, NULL);
  75. idr_destroy(&vc4file->perfmon.idr);
  76. mutex_unlock(&vc4file->perfmon.lock);
  77. }
  78. int vc4_perfmon_create_ioctl(struct drm_device *dev, void *data,
  79. struct drm_file *file_priv)
  80. {
  81. struct vc4_file *vc4file = file_priv->driver_priv;
  82. struct drm_vc4_perfmon_create *req = data;
  83. struct vc4_perfmon *perfmon;
  84. unsigned int i;
  85. int ret;
  86. /* Number of monitored counters cannot exceed HW limits. */
  87. if (req->ncounters > DRM_VC4_MAX_PERF_COUNTERS ||
  88. !req->ncounters)
  89. return -EINVAL;
  90. /* Make sure all events are valid. */
  91. for (i = 0; i < req->ncounters; i++) {
  92. if (req->events[i] >= VC4_PERFCNT_NUM_EVENTS)
  93. return -EINVAL;
  94. }
  95. perfmon = kzalloc(sizeof(*perfmon) + (req->ncounters * sizeof(u64)),
  96. GFP_KERNEL);
  97. if (!perfmon)
  98. return -ENOMEM;
  99. for (i = 0; i < req->ncounters; i++)
  100. perfmon->events[i] = req->events[i];
  101. perfmon->ncounters = req->ncounters;
  102. refcount_set(&perfmon->refcnt, 1);
  103. mutex_lock(&vc4file->perfmon.lock);
  104. ret = idr_alloc(&vc4file->perfmon.idr, perfmon, VC4_PERFMONID_MIN,
  105. VC4_PERFMONID_MAX, GFP_KERNEL);
  106. mutex_unlock(&vc4file->perfmon.lock);
  107. if (ret < 0) {
  108. kfree(perfmon);
  109. return ret;
  110. }
  111. req->id = ret;
  112. return 0;
  113. }
  114. int vc4_perfmon_destroy_ioctl(struct drm_device *dev, void *data,
  115. struct drm_file *file_priv)
  116. {
  117. struct vc4_file *vc4file = file_priv->driver_priv;
  118. struct drm_vc4_perfmon_destroy *req = data;
  119. struct vc4_perfmon *perfmon;
  120. mutex_lock(&vc4file->perfmon.lock);
  121. perfmon = idr_remove(&vc4file->perfmon.idr, req->id);
  122. mutex_unlock(&vc4file->perfmon.lock);
  123. if (!perfmon)
  124. return -EINVAL;
  125. vc4_perfmon_put(perfmon);
  126. return 0;
  127. }
  128. int vc4_perfmon_get_values_ioctl(struct drm_device *dev, void *data,
  129. struct drm_file *file_priv)
  130. {
  131. struct vc4_file *vc4file = file_priv->driver_priv;
  132. struct drm_vc4_perfmon_get_values *req = data;
  133. struct vc4_perfmon *perfmon;
  134. int ret;
  135. mutex_lock(&vc4file->perfmon.lock);
  136. perfmon = idr_find(&vc4file->perfmon.idr, req->id);
  137. vc4_perfmon_get(perfmon);
  138. mutex_unlock(&vc4file->perfmon.lock);
  139. if (!perfmon)
  140. return -EINVAL;
  141. if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->counters,
  142. perfmon->ncounters * sizeof(u64)))
  143. ret = -EFAULT;
  144. else
  145. ret = 0;
  146. vc4_perfmon_put(perfmon);
  147. return ret;
  148. }