blk-sec-stat.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Samsung Block Statistics
  4. *
  5. * Copyright (C) 2021 Manjong Lee <[email protected]>
  6. * Copyright (C) 2021 Junho Kim <[email protected]>
  7. * Copyright (C) 2021 Changheun Lee <[email protected]>
  8. * Copyright (C) 2021 Seunghwan Hyun <[email protected]>
  9. * Copyright (C) 2021 Tran Xuan Nam <[email protected]>
  10. */
  11. #include <linux/sysfs.h>
  12. #include <linux/module.h>
  13. #include <linux/blk_types.h>
  14. #include <linux/blkdev.h>
  15. #include <linux/blk-mq.h>
  16. #include <linux/part_stat.h>
  17. #include "blk-sec.h"
  18. struct accumulated_stat {
  19. struct timespec64 uptime;
  20. unsigned long sectors[3]; /* READ, WRITE, DISCARD */
  21. unsigned long ios[3];
  22. unsigned long iot;
  23. };
  24. static struct accumulated_stat old, new;
  25. extern int blk_sec_stat_pio_init(struct kobject *kobj);
  26. extern void blk_sec_stat_pio_exit(struct kobject *kobj);
  27. extern struct pio_node *get_pio_node(struct request *rq);
  28. extern void update_pio_node(struct request *rq,
  29. unsigned int data_size, struct pio_node *pio);
  30. extern void put_pio_node(struct pio_node *pio);
  31. extern int blk_sec_stat_traffic_init(struct kobject *kobj);
  32. extern void blk_sec_stat_traffic_exit(struct kobject *kobj);
  33. extern void blk_sec_stat_traffic_update(struct request *rq,
  34. unsigned int data_size);
  35. void blk_sec_stat_account_init(struct request_queue *q)
  36. {
  37. if (!blk_sec_internal_disk())
  38. pr_err("%s: Can't find internal disk info!", __func__);
  39. }
  40. EXPORT_SYMBOL(blk_sec_stat_account_init);
  41. void blk_sec_stat_account_exit(struct elevator_queue *eq)
  42. {
  43. }
  44. EXPORT_SYMBOL(blk_sec_stat_account_exit);
  45. #define UNSIGNED_DIFF(n, o) (((n) >= (o)) ? ((n) - (o)) : ((n) + (0 - (o))))
  46. #define SECTORS2KB(x) ((x) / 2)
  47. static inline void get_monotonic_boottime(struct timespec64 *ts)
  48. {
  49. *ts = ktime_to_timespec64(ktime_get_boottime());
  50. }
  51. static ssize_t diskios_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
  52. {
  53. struct gendisk *gd = blk_sec_internal_disk();
  54. struct block_device *bdev;
  55. long hours;
  56. int ret;
  57. if (unlikely(!gd))
  58. return -EINVAL;
  59. bdev = gd->part0;
  60. new.ios[STAT_READ] = part_stat_read(bdev, ios[STAT_READ]);
  61. new.ios[STAT_WRITE] = part_stat_read(bdev, ios[STAT_WRITE]);
  62. new.ios[STAT_DISCARD] = part_stat_read(bdev, ios[STAT_DISCARD]);
  63. new.sectors[STAT_READ] = part_stat_read(bdev, sectors[STAT_READ]);
  64. new.sectors[STAT_WRITE] = part_stat_read(bdev, sectors[STAT_WRITE]);
  65. new.sectors[STAT_DISCARD] = part_stat_read(bdev, sectors[STAT_DISCARD]);
  66. new.iot = jiffies_to_msecs(part_stat_read(bdev, io_ticks)) / 1000;
  67. get_monotonic_boottime(&(new.uptime));
  68. hours = (new.uptime.tv_sec - old.uptime.tv_sec) / 60;
  69. hours = (hours + 30) / 60;
  70. ret = sprintf(buf, "\"ReadC\":\"%lu\",\"ReadKB\":\"%lu\","
  71. "\"WriteC\":\"%lu\",\"WriteKB\":\"%lu\","
  72. "\"DiscardC\":\"%lu\",\"DiscardKB\":\"%lu\","
  73. "\"IOT\":\"%lu\","
  74. "\"Hours\":\"%ld\"\n",
  75. UNSIGNED_DIFF(new.ios[STAT_READ], old.ios[STAT_READ]),
  76. SECTORS2KB(UNSIGNED_DIFF(new.sectors[STAT_READ], old.sectors[STAT_READ])),
  77. UNSIGNED_DIFF(new.ios[STAT_WRITE], old.ios[STAT_WRITE]),
  78. SECTORS2KB(UNSIGNED_DIFF(new.sectors[STAT_WRITE], old.sectors[STAT_WRITE])),
  79. UNSIGNED_DIFF(new.ios[STAT_DISCARD], old.ios[STAT_DISCARD]),
  80. SECTORS2KB(UNSIGNED_DIFF(new.sectors[STAT_DISCARD], old.sectors[STAT_DISCARD])),
  81. UNSIGNED_DIFF(new.iot, old.iot),
  82. hours);
  83. old.ios[STAT_READ] = new.ios[STAT_READ];
  84. old.ios[STAT_WRITE] = new.ios[STAT_WRITE];
  85. old.ios[STAT_DISCARD] = new.ios[STAT_DISCARD];
  86. old.sectors[STAT_READ] = new.sectors[STAT_READ];
  87. old.sectors[STAT_WRITE] = new.sectors[STAT_WRITE];
  88. old.sectors[STAT_DISCARD] = new.sectors[STAT_DISCARD];
  89. old.uptime = new.uptime;
  90. old.iot = new.iot;
  91. return ret;
  92. }
  93. static inline bool may_account_rq(struct request *rq)
  94. {
  95. struct gendisk *gd = blk_sec_internal_disk();
  96. if (unlikely(!gd))
  97. return false;
  98. if (gd->queue != rq->q)
  99. return false;
  100. return true;
  101. }
  102. void blk_sec_stat_account_io_prepare(struct request *rq, void *ptr_pio)
  103. {
  104. if (unlikely(!may_account_rq(rq)))
  105. return;
  106. *(struct pio_node **)ptr_pio = get_pio_node(rq);
  107. }
  108. EXPORT_SYMBOL(blk_sec_stat_account_io_prepare);
  109. void blk_sec_stat_account_io_complete(struct request *rq,
  110. unsigned int data_size, void *pio)
  111. {
  112. if (unlikely(!may_account_rq(rq)))
  113. return;
  114. blk_sec_stat_traffic_update(rq, data_size);
  115. update_pio_node(rq, data_size, (struct pio_node *)pio);
  116. }
  117. EXPORT_SYMBOL(blk_sec_stat_account_io_complete);
  118. void blk_sec_stat_account_io_finish(struct request *rq, void *ptr_pio)
  119. {
  120. if (unlikely(!may_account_rq(rq)))
  121. return;
  122. put_pio_node(*(struct pio_node **)ptr_pio);
  123. *(struct pio_node **)ptr_pio = NULL;
  124. }
  125. EXPORT_SYMBOL(blk_sec_stat_account_io_finish);
  126. static struct kobj_attribute diskios_attr = __ATTR(diskios, 0444, diskios_show, NULL);
  127. static const struct attribute *blk_sec_stat_attrs[] = {
  128. &diskios_attr.attr,
  129. NULL,
  130. };
  131. static struct kobject *blk_sec_stats_kobj;
  132. static int __init blk_sec_stats_init(void)
  133. {
  134. int retval;
  135. blk_sec_stats_kobj = kobject_create_and_add("blk_sec_stats", kernel_kobj);
  136. if (!blk_sec_stats_kobj)
  137. return -ENOMEM;
  138. retval = sysfs_create_files(blk_sec_stats_kobj, blk_sec_stat_attrs);
  139. if (retval) {
  140. kobject_put(blk_sec_stats_kobj);
  141. return retval;
  142. }
  143. retval = blk_sec_stat_pio_init(blk_sec_stats_kobj);
  144. if (retval)
  145. pr_err("%s: fail to initialize PIO sub module", __func__);
  146. retval = blk_sec_stat_traffic_init(blk_sec_stats_kobj);
  147. if (retval)
  148. pr_err("%s: fail to initialize TRAFFIC sub module", __func__);
  149. return 0;
  150. }
  151. static void __exit blk_sec_stats_exit(void)
  152. {
  153. blk_sec_stat_traffic_exit(blk_sec_stats_kobj);
  154. blk_sec_stat_pio_exit(blk_sec_stats_kobj);
  155. sysfs_remove_files(blk_sec_stats_kobj, blk_sec_stat_attrs);
  156. kobject_put(blk_sec_stats_kobj);
  157. }
  158. module_init(blk_sec_stats_init);
  159. module_exit(blk_sec_stats_exit);
  160. MODULE_LICENSE("GPL v2");
  161. MODULE_AUTHOR("Manjong Lee <[email protected]>");
  162. MODULE_DESCRIPTION("Samsung block layer statistics module for various purposes");
  163. MODULE_VERSION("1.0");