Whamcloud - gitweb
LU-16605 lfs: Add -n option to fid2path
[fs/lustre-release.git] / lustre / utils / ofd_access_batch.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  *
22  * Copyright 2020, DataDirect Networks Storage.
23  *
24  * This file is part of Lustre, http://www.lustre.org/
25  *
26  * Author: John L. Hammond <jhammond@whamcloud.com>
27  *
28  * lustre/utils/ofd_access_batch.c
29  *
30  * Access log entry batching for ofd_access_log_reader.
31  */
32 #include <stdlib.h>
33 #include <stdbool.h>
34 #include <stddef.h>
35 #include <assert.h>
36 #include <malloc.h>
37 #include <unistd.h>
38 #include <pthread.h>
39 #include <linux/lustre/lustre_access_log.h>
40 #include <linux/lustre/lustre_fid.h>
41 #include <linux/lustre/lustre_idl.h>
42 #include <libcfs/util/hash.h>
43 #include <libcfs/util/list.h>
44 #include <lustre/lustreapi.h>
45 #include "lstddef.h"
46 #include "ofd_access_batch.h"
47
48 struct fid_hash_node {
49         struct list_head fhn_node;
50         struct lu_fid fhn_fid;
51 };
52
53 static inline bool fid_eq(const struct lu_fid *f1, const struct lu_fid *f2)
54 {
55         return f1->f_seq == f2->f_seq && f1->f_oid == f2->f_oid &&
56                f1->f_ver == f2->f_ver;
57 }
58
59 static void fhn_init(struct fid_hash_node *fhn, const struct lu_fid *fid)
60 {
61         INIT_LIST_HEAD(&fhn->fhn_node);
62         fhn->fhn_fid = *fid;
63 }
64
65 static bool fhn_is_hashed(struct fid_hash_node *fhn)
66 {
67         return !list_empty(&fhn->fhn_node);
68 }
69
70 static void fhn_del_init(struct fid_hash_node *fhn)
71 {
72         if (fhn_is_hashed(fhn))
73                 list_del_init(&fhn->fhn_node);
74 }
75
76 static inline void fhn_replace_init(struct fid_hash_node *old_fhn,
77                                 struct fid_hash_node *new_fhn)
78 {
79         list_add(&new_fhn->fhn_node, &old_fhn->fhn_node);
80         list_del_init(&old_fhn->fhn_node);
81 }
82
83 void fid_hash_add(struct list_head *head, unsigned int shift,
84                 struct fid_hash_node *fhn)
85 {
86         assert(!fhn_is_hashed(fhn));
87
88         list_add(&fhn->fhn_node, &head[llapi_fid_hash(&fhn->fhn_fid, shift)]);
89 }
90
91 struct fid_hash_node *
92 fid_hash_find(struct list_head *head, unsigned int shift, const struct lu_fid *fid)
93 {
94         struct list_head *hash_list;
95         struct fid_hash_node *fhn, *next;
96
97         hash_list = &head[llapi_fid_hash(fid, shift)];
98         list_for_each_entry_safe(fhn, next, hash_list, fhn_node) {
99                 assert(fhn_is_hashed(fhn));
100
101                 if (fid_eq(fid, &fhn->fhn_fid))
102                         return fhn;
103         }
104
105         return NULL;
106 }
107
108 struct fid_hash_node *
109 fid_hash_insert(struct list_head *head, unsigned int shift, struct fid_hash_node *new_fhn)
110 {
111         struct list_head *list;
112         struct fid_hash_node *old_fhn, *next;
113
114         list = &head[llapi_fid_hash(&new_fhn->fhn_fid, shift)];
115         list_for_each_entry_safe(old_fhn, next, list, fhn_node) {
116                 assert(fhn_is_hashed(old_fhn));
117
118                 if (fid_eq(&old_fhn->fhn_fid, &new_fhn->fhn_fid))
119                         return old_fhn;
120         }
121
122         list_add(&new_fhn->fhn_node, list);
123
124         return new_fhn;
125 }
126
127 int fid_hash_init(struct list_head **phead, unsigned int *pshift, unsigned int shift)
128 {
129         struct list_head *new_head;
130         unsigned int i;
131
132         new_head = malloc(sizeof(*new_head) << shift);
133         if (new_head == NULL)
134                 return -1;
135
136         for (i = 0; i < (1 << shift); i++)
137                 INIT_LIST_HEAD(&new_head[i]);
138
139         *phead = new_head;
140         *pshift = shift;
141
142         return 0;
143 }
144
145 int fid_hash_resize(struct list_head **phead, unsigned int *pshift, unsigned int new_shift)
146 {
147         struct list_head *new_head;
148         unsigned int i;
149         int rc;
150
151         if (*pshift == new_shift)
152                 return 0;
153
154         rc = fid_hash_init(&new_head, &new_shift, new_shift);
155         if (rc < 0)
156                 return rc;
157
158         for (i = 0; i < (1 << *pshift); i++) {
159                 struct list_head *list = &(*phead)[i];
160                 struct fid_hash_node *fhn, *next;
161
162                 list_for_each_entry_safe(fhn, next, list, fhn_node) {
163                         fhn_del_init(fhn);
164                         fid_hash_add(new_head, new_shift, fhn);
165                 }
166         }
167
168         free(*phead);
169         *phead = new_head;
170         *pshift = new_shift;
171
172         return 0;
173 }
174
175 enum {
176         ALR_READ = 0,
177         ALR_WRITE = 1,
178 };
179
180 /* Entry in the batching hash. */
181 struct alr_entry {
182         struct fid_hash_node alre_fid_hash_node;
183         time_t alre_time[2]; /* Not strictly needed. */
184         __u64 alre_begin[2];
185         __u64 alre_end[2];
186         __u64 alre_size[2];
187         __u64 alre_segment_count[2];
188         __u64 alre_count[2];
189         char alre_obd_name[];
190 };
191
192 enum {
193         ALR_BATCH_HASH_SHIFT_DEFAULT = 10,
194         ALR_BATCH_HASH_SHIFT_MAX = 30,
195 };
196
197 struct alr_batch {
198         struct list_head *alrb_hash;
199         unsigned int alrb_hash_shift;
200         unsigned int alrb_count;
201 };
202
203 static void alre_del_init(struct alr_entry *alre)
204 {
205         fhn_del_init(&alre->alre_fid_hash_node);
206 }
207
208 static void alre_update(struct alr_entry *alre, time_t time, __u64 begin,
209                         __u64 end, __u32 size, __u32 segment_count, __u32 flags)
210 {
211         unsigned int d = (flags & OFD_ACCESS_READ) ? ALR_READ : ALR_WRITE;
212
213         alre->alre_time[d] = max_t(time_t, alre->alre_time[d], time);
214         alre->alre_begin[d] = min_t(__u64, alre->alre_begin[d], begin);
215         alre->alre_end[d] = max_t(__u64, alre->alre_end[d], end);
216         alre->alre_size[d] += size;
217         alre->alre_segment_count[d] += segment_count;
218         alre->alre_count[d] += 1;
219 }
220
221 int alr_batch_add(struct alr_batch *alrb, const char *obd_name,
222                 const struct lu_fid *pfid, time_t time, __u64 begin, __u64 end,
223                 __u32 size, __u32 segment_count, __u32 flags)
224 {
225         struct fid_hash_node fhn, *p;
226         struct alr_entry *alre;
227         int rc;
228
229         if (alrb == NULL)
230                 return 0;
231
232         assert(sizeof(time_t) == sizeof(__u64));
233
234         fhn_init(&fhn, pfid);
235
236         /* Find old or insert sentinel (fhn). Replace sentinel if returned. */
237         p = fid_hash_insert(alrb->alrb_hash, alrb->alrb_hash_shift, &fhn);
238         if (p == &fhn) {
239                 size_t alre_size = sizeof(*alre) + strlen(obd_name) + 1;
240
241                 alre = calloc(1, alre_size);
242                 if (alre == NULL) {
243                         rc = -1;
244                         goto out;
245                 }
246
247                 fhn_init(&alre->alre_fid_hash_node, pfid);
248                 strcpy(alre->alre_obd_name, obd_name);
249                 fhn_replace_init(&fhn, &alre->alre_fid_hash_node);
250                 alrb->alrb_count++;
251         } else {
252                 alre = container_of(p, struct alr_entry, alre_fid_hash_node);
253         }
254
255         alre_update(alre, time, begin, end, size, segment_count, flags);
256         rc = 0;
257 out:
258         fhn_del_init(&fhn);
259
260         return rc;
261 }
262
263 int sort_compare(const void *a1, const void *a2)
264 {
265         int l = *(const int*)a1;
266         int r = *(const int *)a2;
267         if (l > r) return -1;
268         if (l < r) return  1;
269         return 0;
270 }
271
272 static void alre_printf(FILE *f, struct alr_entry *alre, int d)
273 {
274         fprintf(f, "o=%s f="DFID" t=%lld b=%llu e=%llu s=%llu g=%llu n=%llu d=%c\n",
275                 alre->alre_obd_name,
276                 PFID(&alre->alre_fid_hash_node.fhn_fid),
277                 (long long)alre->alre_time[d],
278                 (unsigned long long)alre->alre_begin[d],
279                 (unsigned long long)alre->alre_end[d],
280                 (unsigned long long)alre->alre_size[d],
281                 (unsigned long long)alre->alre_segment_count[d],
282                 (unsigned long long)alre->alre_count[d],
283                 (d == ALR_READ) ? 'r' : 'w');
284 }
285
286 struct alr_thread_arg {
287         struct list_head list;
288         int fraction;
289         FILE *file;
290         pthread_mutex_t *file_mutex;
291 };
292
293 void *alr_sort_and_print_thread(void *arg)
294 {
295         struct alr_entry *alre, *next;
296         struct alr_thread_arg *aa = arg;
297         struct list_head *tmp = &aa->list;
298         int *sa = NULL;
299         int rc, d, i, nr = 0;
300         unsigned long cut;
301
302         list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
303                 if (alre->alre_count[0] > 0)
304                         nr++;
305                 if (alre->alre_count[1] > 0)
306                         nr++;
307         }
308
309         if (nr == 0)
310                 goto out;
311
312         sa = calloc(nr, sizeof(*sa));
313         if (!sa) {
314                 fprintf(stderr, "cannot allocate memory for sorting\n");
315                 exit(1);
316         }
317
318         i = 0;
319         list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
320                 if (alre->alre_count[0] > 0)
321                         sa[i++] = alre->alre_count[0];
322                 if (alre->alre_count[1] > 0)
323                         sa[i++] = alre->alre_count[1];
324         }
325
326         qsort(sa, nr, sizeof(*sa), sort_compare);
327         i = nr * aa->fraction / 100;
328         cut = sa[i];
329         if (cut < 1)
330                 cut = 1;
331         free(sa);
332
333         /* Prevent jumbled output from multiple concurrent sort and
334          * print threads. */
335         rc = pthread_mutex_lock(aa->file_mutex);
336         if (rc != 0) {
337                 fprintf(stderr, "cannot lock batch file: %s\n",
338                         strerror(rc));
339                 exit(1);
340         }
341
342         /* there might be lots of items at @cut, but we want to limit total
343          * output. so the first loop dumps all items > @cut and the second
344          * loop dumps items=@cut so that total number (@i) is not exceeeded.
345          * XXX: possible optimization - move items=@cut to another list, so
346          * that 2nd pass takes < O(n) */
347         list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
348                 for (d = 0; d < 2; d++) {
349                         if (alre->alre_count[d] <= cut)
350                                 continue;
351                         alre_printf(aa->file, alre, d);
352                         i--;
353                 }
354         }
355
356         list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
357                 for (d = 0; d < 2 && i > 0; d++) {
358                         if (alre->alre_count[d] != cut)
359                                 continue;
360                         alre_printf(aa->file, alre, d);
361                         i--;
362                 }
363         }
364
365         rc = pthread_mutex_unlock(aa->file_mutex);
366         if (rc != 0) {
367                 fprintf(stderr, "cannot unlock batch file: %s\n",
368                         strerror(rc));
369                 exit(1);
370         }
371
372 out:
373         fflush(aa->file);
374
375         list_for_each_entry_safe(alre, next, tmp, alre_fid_hash_node.fhn_node) {
376                 alre_del_init(alre);
377                 free(alre);
378         }
379
380         free(aa);
381
382         return NULL;
383 }
384
385 /* Print, clear, and resize the batch. */
386 int alr_batch_print(struct alr_batch *alrb, FILE *file,
387                     pthread_mutex_t *file_mutex, int fraction)
388 {
389         unsigned int new_hash_shift;
390         pthread_attr_t attr, *pattr = NULL;
391         struct alr_thread_arg *aa = NULL;
392         pthread_t pid;
393         int i, rc;
394
395         if (alrb == NULL)
396                 return 0;
397
398         aa = calloc(1, sizeof(*aa));
399         if (aa == NULL)
400                 return -ENOMEM;
401
402         /* move all collected items to the temp list */
403         INIT_LIST_HEAD(&aa->list);
404         for (i = 0; i < (1 << alrb->alrb_hash_shift); i++) {
405                 if (list_empty(&alrb->alrb_hash[i]))
406                         continue;
407                 list_splice(&alrb->alrb_hash[i], &aa->list);
408                 INIT_LIST_HEAD(&alrb->alrb_hash[i]);
409         }
410         aa->file = file;
411         aa->file_mutex = file_mutex;
412         aa->fraction = fraction;
413
414         rc = pthread_attr_init(&attr);
415         if (rc != 0)
416                 goto out;
417
418         pattr = &attr;
419
420         rc = pthread_attr_setdetachstate(pattr, PTHREAD_CREATE_DETACHED);
421         if (rc != 0)
422                 goto out;
423
424         /* as sorting may take time and we don't want to lose access
425          * records we better do sorting and printing in a different thread */
426         rc = pthread_create(&pid, pattr, &alr_sort_and_print_thread, aa);
427         if (rc != 0)
428                 goto out;
429
430         aa = NULL; /* Sort and print thread owns it now. */
431 out:
432         /* Resize hash based on previous count. */
433         new_hash_shift = alrb->alrb_hash_shift;
434
435         while (new_hash_shift < ALR_BATCH_HASH_SHIFT_MAX &&
436                (1 << new_hash_shift) < alrb->alrb_count)
437                 new_hash_shift++;
438
439         fid_hash_resize(&alrb->alrb_hash, &alrb->alrb_hash_shift,
440                         new_hash_shift);
441
442         alrb->alrb_count = 0;
443
444         if (pattr != NULL)
445                 pthread_attr_destroy(pattr);
446
447         if (aa != NULL) {
448                 struct alr_entry *alre, *next;
449
450                 list_for_each_entry_safe(alre, next, &aa->list,
451                                          alre_fid_hash_node.fhn_node) {
452                         alre_del_init(alre);
453                         free(alre);
454                 }
455         }
456
457         free(aa);
458
459         if (rc > 0)
460                 rc = -rc; /* Fixup pthread return conventions. */
461
462         return rc;
463 }
464
465 struct alr_batch *alr_batch_create(unsigned int shift)
466 {
467         struct alr_batch *alrb;
468         int rc;
469
470         if (shift == -1U)
471                 shift = ALR_BATCH_HASH_SHIFT_DEFAULT;
472
473         alrb = calloc(1, sizeof(*alrb));
474         if (alrb == NULL)
475                 return NULL;
476
477         rc = fid_hash_init(&alrb->alrb_hash, &alrb->alrb_hash_shift, shift);
478         if (rc < 0) {
479                 free(alrb);
480                 return NULL;
481         }
482
483         return alrb;
484 }
485
486 void alr_batch_destroy(struct alr_batch *alrb)
487 {
488         unsigned int i;
489
490         if (alrb == NULL)
491                 return;
492
493         for (i = 0; i < (1 << alrb->alrb_hash_shift); i++) {
494                 struct list_head *list = &alrb->alrb_hash[i];
495                 struct alr_entry *alre, *next;
496
497                 list_for_each_entry_safe(alre, next, list, alre_fid_hash_node.fhn_node) {
498                         alre_del_init(alre);
499                         free(alre);
500                 }
501         }
502
503         free(alrb->alrb_hash);
504         free(alrb);
505 }