Add per-device hierarchical event counters to track when RDMA resource limits are exceeded. The rdma.events file reports max event counts propagated upward from the cgroup whose limit was hit to all ancestors. This mirrors the design of pids.events, where events are attributed to the cgroup that imposed the limit, not necessarily the cgroup where the allocation was attempted. Userspace can monitor this file via poll/epoll for real-time notification of resource exhaustion.
Unified · Split
| @@ -24,6 +24,9 @@ struct rdma_cgroup { · 23 unmodified lines | |||
| 24 | * that belongs to this cgroup. | 24 | * that belongs to this cgroup. |
| 25 | */ | 25 | */ |
| 26 | struct list_head rpools; | 26 | struct list_head rpools; |
| 27 | |||
| 28 | /* Handle for rdma.events */ | ||
| 29 | struct cgroup_file events_file; | ||
| 27 | }; | 30 | }; |
| 28 | 31 | ||
| 29 | struct rdmacg_device { | 32 | struct rdmacg_device { |
| @@ -81,6 +81,9 @@ struct rdmacg_resource_pool { · 80 unmodified lines | |||
| 81 | u64 usage_sum; | 81 | u64 usage_sum; |
| 82 | /* total number counts which are set to max */ | 82 | /* total number counts which are set to max */ |
| 83 | int num_max_cnt; | 83 | int num_max_cnt; |
| 84 | |||
| 85 | /* per-resource hierarchical max event counters */ | ||
| 86 | u64 events_max[RDMACG_RESOURCE_MAX]; | ||
| 84 | }; | 87 | }; |
| 85 | 88 | ||
| 86 | static struct rdma_cgroup *css_rdmacg(struct cgroup_subsys_state *css) | 89 | static struct rdma_cgroup *css_rdmacg(struct cgroup_subsys_state *css) |
| @@ -214,7 +217,8 @@ uncharge_cg_locked(struct rdma_cgroup *cg, · 127 unmodified lines | |||
| 214 | * watermark even after all resources are freed. | 217 | * watermark even after all resources are freed. |
| 215 | */ | 218 | */ |
| 216 | for (i = 0; i < RDMACG_RESOURCE_MAX; i++) { | 219 | for (i = 0; i < RDMACG_RESOURCE_MAX; i++) { |
| 217 | if (rpool->resources[i].peak) | 220 | if (rpool->resources[i].peak || |
| 221 | READ_ONCE(rpool->events_max[i])) | ||
| 218 | return; | 222 | return; |
| 219 | } | 223 | } |
| 220 | /* | 224 | /* |
| @@ -225,6 +229,34 @@ uncharge_cg_locked(struct rdma_cgroup *cg, · 4 unmodified lines | |||
| 225 | } | 229 | } |
| 226 | } | 230 | } |
| 227 | 231 | ||
| 232 | /** | ||
| 233 | * rdmacg_event_locked - fire hierarchical max event when resource limit is hit | ||
| 234 | * @over_cg: cgroup whose limit was exceeded | ||
| 235 | * @device: rdma device | ||
| 236 | * @index: resource type index | ||
| 237 | * | ||
| 238 | * Must be called under rdmacg_mutex. Propagates max event counts | ||
| 239 | * from @over_cg (including itself) upward to all ancestors with | ||
| 240 | * an rpool and notifies userspace. | ||
| 241 | */ | ||
| 242 | static void rdmacg_event_locked(struct rdma_cgroup *over_cg, | ||
| 243 | struct rdmacg_device *device, | ||
| 244 | enum rdmacg_resource_type index) | ||
| 245 | { | ||
| 246 | struct rdmacg_resource_pool *rpool; | ||
| 247 | struct rdma_cgroup *p; | ||
| 248 | |||
| 249 | lockdep_assert_held(&rdmacg_mutex); | ||
| 250 | |||
| 251 | for (p = over_cg; parent_rdmacg(p); p = parent_rdmacg(p)) { | ||
| 252 | rpool = get_cg_rpool_locked(p, device); | ||
| 253 | if (!IS_ERR(rpool)) { | ||
| 254 | rpool->events_max[index]++; | ||
| 255 | cgroup_file_notify(&p->events_file); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 228 | /** | 260 | /** |
| 229 | * rdmacg_uncharge_hierarchy - hierarchically uncharge rdma resource count | 261 | * rdmacg_uncharge_hierarchy - hierarchically uncharge rdma resource count |
| 230 | * @cg: pointer to cg to uncharge and all parents in hierarchy | 262 | * @cg: pointer to cg to uncharge and all parents in hierarchy |
| @@ -335,6 +367,8 @@ int rdmacg_try_charge(struct rdma_cgroup **rdmacg, · 104 unmodified lines | |||
| 335 | return 0; | 367 | return 0; |
| 336 | 368 | ||
| 337 | err: | 369 | err: |
| 370 | if (ret == -EAGAIN) | ||
| 371 | rdmacg_event_locked(p, device, index); | ||
| 338 | mutex_unlock(&rdmacg_mutex); | 372 | mutex_unlock(&rdmacg_mutex); |
| 339 | rdmacg_uncharge_hierarchy(cg, device, p, index); | 373 | rdmacg_uncharge_hierarchy(cg, device, p, index); |
| 340 | return ret; | 374 | return ret; |
| @@ -494,7 +528,8 @@ static ssize_t rdmacg_resource_set_max(struct kernfs_open_file *of, · 153 unmodified lines | |||
| 494 | int i; | 528 | int i; |
| 495 | 529 | ||
| 496 | for (i = 0; i < RDMACG_RESOURCE_MAX; i++) { | 530 | for (i = 0; i < RDMACG_RESOURCE_MAX; i++) { |
| 497 | if (rpool->resources[i].peak) | 531 | if (rpool->resources[i].peak || |
| 532 | READ_ONCE(rpool->events_max[i])) | ||
| 498 | goto dev_err; | 533 | goto dev_err; |
| 499 | } | 534 | } |
| 500 | /* | 535 | /* |
| @@ -569,6 +604,33 @@ static int rdmacg_resource_read(struct seq_file *sf, void *v) · 68 unmodified lines | |||
| 569 | return 0; | 604 | return 0; |
| 570 | } | 605 | } |
| 571 | 606 | ||
| 607 | static int rdmacg_events_show(struct seq_file *sf, void *v) | ||
| 608 | { | ||
| 609 | struct rdma_cgroup *cg = css_rdmacg(seq_css(sf)); | ||
| 610 | struct rdmacg_resource_pool *rpool; | ||
| 611 | struct rdmacg_device *device; | ||
| 612 | int i; | ||
| 613 | |||
| 614 | mutex_lock(&rdmacg_mutex); | ||
| 615 | |||
| 616 | list_for_each_entry(device, &rdmacg_devices, dev_node) { | ||
| 617 | rpool = find_cg_rpool_locked(cg, device); | ||
| 618 | |||
| 619 | seq_printf(sf, "%s ", device->name); | ||
| 620 | for (i = 0; i < RDMACG_RESOURCE_MAX; i++) { | ||
| 621 | seq_printf(sf, "%s.max=%llu", | ||
| 622 | rdmacg_resource_names[i], | ||
| 623 | rpool ? READ_ONCE(rpool->events_max[i]) : 0ULL); | ||
| 624 | if (i < RDMACG_RESOURCE_MAX - 1) | ||
| 625 | seq_putc(sf, ' '); | ||
| 626 | } | ||
| 627 | seq_putc(sf, '\n'); | ||
| 628 | } | ||
| 629 | |||
| 630 | mutex_unlock(&rdmacg_mutex); | ||
| 631 | return 0; | ||
| 632 | } | ||
| 633 | |||
| 572 | static struct cftype rdmacg_files[] = { | 634 | static struct cftype rdmacg_files[] = { |
| 573 | { | 635 | { |
| 574 | .name = "max", | 636 | .name = "max", |
| @@ -589,6 +651,12 @@ static struct cftype rdmacg_files[] = { · 14 unmodified lines | |||
| 589 | .private = RDMACG_RESOURCE_TYPE_PEAK, | 651 | .private = RDMACG_RESOURCE_TYPE_PEAK, |
| 590 | .flags = CFTYPE_NOT_ON_ROOT, | 652 | .flags = CFTYPE_NOT_ON_ROOT, |
| 591 | }, | 653 | }, |
| 654 | { | ||
| 655 | .name = "events", | ||
| 656 | .seq_show = rdmacg_events_show, | ||
| 657 | .file_offset = offsetof(struct rdma_cgroup, events_file), | ||
| 658 | .flags = CFTYPE_NOT_ON_ROOT, | ||
| 659 | }, | ||
| 592 | { } /* terminate */ | 660 | { } /* terminate */ |
| 593 | }; | 661 | }; |
| 594 | 662 | ||