vm: fix a null dereference on out-of-memory
[minix.git] / lib / libblockdriver / trace.c
blobbf80cc836b93e5d6de051c6c05a088764fcaecad
1 /* This file implements block level tracing support. */
3 #include <minix/drivers.h>
4 #include <minix/blockdriver_mt.h>
5 #include <minix/btrace.h>
6 #include <sys/ioc_block.h>
7 #include <minix/minlib.h>
8 #include <assert.h>
10 #include "const.h"
11 #include "trace.h"
13 #define NO_TRACEDEV ((dev_t) -1)
14 #define NO_TIME ((u32_t) -1)
16 static int trace_enabled = FALSE;
17 static dev_t trace_dev = NO_TRACEDEV;
18 static btrace_entry *trace_buf = NULL;
19 static size_t trace_size = 0;
20 static size_t trace_pos;
21 static size_t trace_next;
22 static u64_t trace_tsc;
24 /* Pointers to in-progress trace entries for each thread (all worker threads,
25 * plus one for the main thread). Each pointer is set to NULL whenever no
26 * operation is currently being traced for that thread, for whatever reason.
28 static btrace_entry *trace_ptr[MAX_THREADS + 1] = { NULL };
30 /*===========================================================================*
31 * trace_gettime *
32 *===========================================================================*/
33 static u32_t trace_gettime(void)
35 /* Return the current time, in microseconds since the start of the trace.
37 u64_t tsc;
39 assert(trace_enabled);
41 read_tsc_64(&tsc);
43 tsc = sub64(tsc, trace_tsc);
45 return tsc_64_to_micros(tsc);
48 /*===========================================================================*
49 * trace_ctl *
50 *===========================================================================*/
51 int trace_ctl(dev_t minor, unsigned int request, endpoint_t endpt,
52 cp_grant_id_t grant)
54 /* Process a block trace control request.
56 size_t size;
57 int r, ctl, entries;
59 switch (request) {
60 case BIOCTRACEBUF:
61 /* The size cannot be changed when tracing is enabled. */
62 if (trace_enabled) return EBUSY;
64 /* Copy in the requested size. */
65 if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &size,
66 sizeof(size))) != OK)
67 return r;
69 if (size >= INT_MAX / sizeof(btrace_entry)) return EINVAL;
71 /* The size can only be set or reset, not changed. */
72 if (size > 0 && trace_size > 0) return EBUSY;
74 /* Allocate or free a buffer for tracing data. For future multi-device
75 * tracing support, the buffer is associated with a minor device.
77 if (size == 0) {
78 if (trace_dev == NO_TRACEDEV) return OK;
80 if (trace_dev != minor) return EINVAL;
82 free(trace_buf);
84 trace_dev = NO_TRACEDEV;
85 } else {
86 if ((trace_buf = malloc(size * sizeof(btrace_entry))) == NULL)
87 return errno;
89 trace_dev = minor;
92 trace_size = size;
93 trace_pos = 0;
94 trace_next = 0;
96 return OK;
98 case BIOCTRACECTL:
99 /* We can only start/stop tracing if the given device has a trace
100 * buffer associated with it.
102 if (trace_dev != minor) return EINVAL;
104 /* Copy in the request code. */
105 if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &ctl,
106 sizeof(ctl))) != OK)
107 return r;
109 /* Start or stop tracing. */
110 switch (ctl) {
111 case BTCTL_START:
112 if (trace_enabled) return EBUSY;
114 read_tsc_64(&trace_tsc);
116 trace_enabled = TRUE;
118 break;
120 case BTCTL_STOP:
121 if (!trace_enabled) return EINVAL;
123 trace_enabled = FALSE;
125 /* Cancel all ongoing trace operations. */
126 memset(trace_ptr, 0, sizeof(trace_ptr));
128 break;
130 default:
131 return EINVAL;
134 return OK;
136 case BIOCTRACEGET:
137 /* We can only retrieve tracing entries if the given device has a trace
138 * buffer associated with it.
140 if (trace_dev != minor) return EINVAL;
142 if (trace_enabled) return EBUSY;
144 /* How much can we copy out? */
145 entries = MIN(trace_pos - trace_next,
146 _MINIX_IOCTL_SIZE_BIG(request) / sizeof(btrace_entry));
148 if (entries == 0)
149 return 0;
151 if ((r = sys_safecopyto(endpt, grant, 0,
152 (vir_bytes) &trace_buf[trace_next],
153 entries * sizeof(btrace_entry))) != OK)
154 return r;
156 trace_next += entries;
158 return entries;
161 return EINVAL;
164 /*===========================================================================*
165 * trace_start *
166 *===========================================================================*/
167 void trace_start(thread_id_t id, message *m_ptr)
169 /* Start creating a trace entry.
171 btrace_entry *entry;
172 int req;
173 u64_t pos;
174 size_t size;
175 int flags;
177 if (!trace_enabled || trace_dev != m_ptr->BDEV_MINOR) return;
179 assert(id >= 0 && id < MAX_THREADS + 1);
181 if (trace_pos == trace_size)
182 return;
184 switch (m_ptr->m_type) {
185 case BDEV_OPEN: req = BTREQ_OPEN; break;
186 case BDEV_CLOSE: req = BTREQ_CLOSE; break;
187 case BDEV_READ: req = BTREQ_READ; break;
188 case BDEV_WRITE: req = BTREQ_WRITE; break;
189 case BDEV_GATHER: req = BTREQ_GATHER; break;
190 case BDEV_SCATTER: req = BTREQ_SCATTER; break;
191 case BDEV_IOCTL: req = BTREQ_IOCTL; break;
192 default: return;
195 switch (m_ptr->m_type) {
196 case BDEV_OPEN:
197 case BDEV_CLOSE:
198 pos = cvu64(0);
199 size = m_ptr->BDEV_ACCESS;
200 flags = 0;
202 break;
204 case BDEV_READ:
205 case BDEV_WRITE:
206 case BDEV_GATHER:
207 case BDEV_SCATTER:
208 pos = make64(m_ptr->BDEV_POS_LO, m_ptr->BDEV_POS_HI);
209 size = m_ptr->BDEV_COUNT;
210 flags = m_ptr->BDEV_FLAGS;
212 break;
214 case BDEV_IOCTL:
215 pos = cvu64(0);
216 size = m_ptr->BDEV_REQUEST;
217 flags = 0;
219 /* Do not log trace control requests. */
220 switch (size) {
221 case BIOCTRACEBUF:
222 case BIOCTRACECTL:
223 case BIOCTRACEGET:
224 return;
227 break;
229 default:
230 /* Do not log any other messages. */
231 return;
234 entry = &trace_buf[trace_pos];
235 entry->request = req;
236 entry->size = size;
237 entry->position = pos;
238 entry->flags = flags;
239 entry->result = BTRES_INPROGRESS;
240 entry->start_time = trace_gettime();
241 entry->finish_time = NO_TIME;
243 trace_ptr[id] = entry;
244 trace_pos++;
247 /*===========================================================================*
248 * trace_setsize *
249 *===========================================================================*/
250 void trace_setsize(thread_id_t id, size_t size)
252 /* Set the current trace entry's actual (byte) size, for vector requests.
254 btrace_entry *entry;
256 if (!trace_enabled) return;
258 assert(id >= 0 && id < MAX_THREADS + 1);
260 if ((entry = trace_ptr[id]) == NULL) return;
262 entry->size = size;
265 /*===========================================================================*
266 * trace_finish *
267 *===========================================================================*/
268 void trace_finish(thread_id_t id, int result)
270 /* Finish a trace entry.
272 btrace_entry *entry;
274 if (!trace_enabled) return;
276 assert(id >= 0 && id < MAX_THREADS + 1);
278 if ((entry = trace_ptr[id]) == NULL) return;
280 entry->result = result;
281 entry->finish_time = trace_gettime();
283 trace_ptr[id] = NULL;