Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1902efe7 | 2 | #include "comm.h" |
a43783ae | 3 | #include <errno.h> |
1902efe7 FW |
4 | #include <stdlib.h> |
5 | #include <stdio.h> | |
72f7c4d2 | 6 | #include <string.h> |
6df74bc0 | 7 | #include <linux/refcount.h> |
f0049f2c | 8 | #include <linux/rbtree.h> |
7f7c536f | 9 | #include <linux/zalloc.h> |
f988e71b | 10 | #include "rwsem.h" |
1902efe7 FW |
11 | |
12 | struct comm_str { | |
13 | char *str; | |
14 | struct rb_node rb_node; | |
6df74bc0 | 15 | refcount_t refcnt; |
1902efe7 FW |
16 | }; |
17 | ||
18 | /* Should perhaps be moved to struct machine */ | |
19 | static struct rb_root comm_str_root; | |
f988e71b | 20 | static struct rw_semaphore comm_str_lock = {.lock = PTHREAD_RWLOCK_INITIALIZER,}; |
1902efe7 | 21 | |
86c19525 | 22 | static struct comm_str *comm_str__get(struct comm_str *cs) |
1902efe7 | 23 | { |
46b3722c JO |
24 | if (cs && refcount_inc_not_zero(&cs->refcnt)) |
25 | return cs; | |
26 | ||
27 | return NULL; | |
1902efe7 FW |
28 | } |
29 | ||
30 | static void comm_str__put(struct comm_str *cs) | |
31 | { | |
6df74bc0 | 32 | if (cs && refcount_dec_and_test(&cs->refcnt)) { |
f988e71b | 33 | down_write(&comm_str_lock); |
1902efe7 | 34 | rb_erase(&cs->rb_node, &comm_str_root); |
f988e71b | 35 | up_write(&comm_str_lock); |
74cf249d | 36 | zfree(&cs->str); |
1902efe7 FW |
37 | free(cs); |
38 | } | |
39 | } | |
40 | ||
41 | static struct comm_str *comm_str__alloc(const char *str) | |
42 | { | |
43 | struct comm_str *cs; | |
44 | ||
45 | cs = zalloc(sizeof(*cs)); | |
46 | if (!cs) | |
47 | return NULL; | |
48 | ||
49 | cs->str = strdup(str); | |
50 | if (!cs->str) { | |
51 | free(cs); | |
52 | return NULL; | |
53 | } | |
54 | ||
6df74bc0 | 55 | refcount_set(&cs->refcnt, 1); |
86c19525 | 56 | |
1902efe7 FW |
57 | return cs; |
58 | } | |
59 | ||
f988e71b KL |
60 | static |
61 | struct comm_str *__comm_str__findnew(const char *str, struct rb_root *root) | |
1902efe7 FW |
62 | { |
63 | struct rb_node **p = &root->rb_node; | |
64 | struct rb_node *parent = NULL; | |
65 | struct comm_str *iter, *new; | |
66 | int cmp; | |
67 | ||
68 | while (*p != NULL) { | |
69 | parent = *p; | |
70 | iter = rb_entry(parent, struct comm_str, rb_node); | |
71 | ||
46b3722c JO |
72 | /* |
73 | * If we race with comm_str__put, iter->refcnt is 0 | |
74 | * and it will be removed within comm_str__put call | |
75 | * shortly, ignore it in this search. | |
76 | */ | |
1902efe7 | 77 | cmp = strcmp(str, iter->str); |
46b3722c JO |
78 | if (!cmp && comm_str__get(iter)) |
79 | return iter; | |
1902efe7 FW |
80 | |
81 | if (cmp < 0) | |
82 | p = &(*p)->rb_left; | |
83 | else | |
84 | p = &(*p)->rb_right; | |
85 | } | |
86 | ||
87 | new = comm_str__alloc(str); | |
88 | if (!new) | |
89 | return NULL; | |
90 | ||
91 | rb_link_node(&new->rb_node, parent, p); | |
92 | rb_insert_color(&new->rb_node, root); | |
93 | ||
94 | return new; | |
95 | } | |
96 | ||
f988e71b KL |
97 | static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root) |
98 | { | |
99 | struct comm_str *cs; | |
100 | ||
101 | down_write(&comm_str_lock); | |
102 | cs = __comm_str__findnew(str, root); | |
103 | up_write(&comm_str_lock); | |
104 | ||
105 | return cs; | |
106 | } | |
107 | ||
65de51f9 | 108 | struct comm *comm__new(const char *str, u64 timestamp, bool exec) |
1902efe7 FW |
109 | { |
110 | struct comm *comm = zalloc(sizeof(*comm)); | |
111 | ||
112 | if (!comm) | |
113 | return NULL; | |
114 | ||
115 | comm->start = timestamp; | |
65de51f9 | 116 | comm->exec = exec; |
1902efe7 FW |
117 | |
118 | comm->comm_str = comm_str__findnew(str, &comm_str_root); | |
119 | if (!comm->comm_str) { | |
120 | free(comm); | |
121 | return NULL; | |
122 | } | |
123 | ||
1902efe7 FW |
124 | return comm; |
125 | } | |
126 | ||
65de51f9 | 127 | int comm__override(struct comm *comm, const char *str, u64 timestamp, bool exec) |
4dfced35 | 128 | { |
3178f58b | 129 | struct comm_str *new, *old = comm->comm_str; |
4dfced35 | 130 | |
3178f58b FW |
131 | new = comm_str__findnew(str, &comm_str_root); |
132 | if (!new) | |
133 | return -ENOMEM; | |
4dfced35 | 134 | |
4dfced35 | 135 | comm_str__put(old); |
3178f58b FW |
136 | comm->comm_str = new; |
137 | comm->start = timestamp; | |
65de51f9 AH |
138 | if (exec) |
139 | comm->exec = true; | |
3178f58b FW |
140 | |
141 | return 0; | |
4dfced35 NK |
142 | } |
143 | ||
1902efe7 FW |
144 | void comm__free(struct comm *comm) |
145 | { | |
146 | comm_str__put(comm->comm_str); | |
147 | free(comm); | |
148 | } | |
149 | ||
150 | const char *comm__str(const struct comm *comm) | |
151 | { | |
152 | return comm->comm_str->str; | |
153 | } |