4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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).
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
22 * Copyright 2020, DataDirect Networks Storage.
24 * This file is part of Lustre, http://www.lustre.org/
26 * Author: John L. Hammond <jhammond@whamcloud.com>
28 * lustre/utils/ofd_access_batch.c
30 * Access log entry batching for ofd_access_log_reader.
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>
46 #include "ofd_access_batch.h"
48 struct fid_hash_node {
49 struct list_head fhn_node;
50 struct lu_fid fhn_fid;
53 static inline bool fid_eq(const struct lu_fid *f1, const struct lu_fid *f2)
55 return f1->f_seq == f2->f_seq && f1->f_oid == f2->f_oid &&
56 f1->f_ver == f2->f_ver;
59 static void fhn_init(struct fid_hash_node *fhn, const struct lu_fid *fid)
61 INIT_LIST_HEAD(&fhn->fhn_node);
65 static bool fhn_is_hashed(struct fid_hash_node *fhn)
67 return !list_empty(&fhn->fhn_node);
70 static void fhn_del_init(struct fid_hash_node *fhn)
72 if (fhn_is_hashed(fhn))
73 list_del_init(&fhn->fhn_node);
76 static inline void fhn_replace_init(struct fid_hash_node *old_fhn,
77 struct fid_hash_node *new_fhn)
79 list_add(&new_fhn->fhn_node, &old_fhn->fhn_node);
80 list_del_init(&old_fhn->fhn_node);
83 void fid_hash_add(struct list_head *head, unsigned int shift,
84 struct fid_hash_node *fhn)
86 assert(!fhn_is_hashed(fhn));
88 list_add(&fhn->fhn_node, &head[llapi_fid_hash(&fhn->fhn_fid, shift)]);
91 struct fid_hash_node *
92 fid_hash_find(struct list_head *head, unsigned int shift, const struct lu_fid *fid)
94 struct list_head *hash_list;
95 struct fid_hash_node *fhn, *next;
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));
101 if (fid_eq(fid, &fhn->fhn_fid))
108 struct fid_hash_node *
109 fid_hash_insert(struct list_head *head, unsigned int shift, struct fid_hash_node *new_fhn)
111 struct list_head *list;
112 struct fid_hash_node *old_fhn, *next;
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));
118 if (fid_eq(&old_fhn->fhn_fid, &new_fhn->fhn_fid))
122 list_add(&new_fhn->fhn_node, list);
127 int fid_hash_init(struct list_head **phead, unsigned int *pshift, unsigned int shift)
129 struct list_head *new_head;
132 new_head = malloc(sizeof(*new_head) << shift);
133 if (new_head == NULL)
136 for (i = 0; i < (1 << shift); i++)
137 INIT_LIST_HEAD(&new_head[i]);
145 int fid_hash_resize(struct list_head **phead, unsigned int *pshift, unsigned int new_shift)
147 struct list_head *new_head;
151 if (*pshift == new_shift)
154 rc = fid_hash_init(&new_head, &new_shift, new_shift);
158 for (i = 0; i < (1 << *pshift); i++) {
159 struct list_head *list = &(*phead)[i];
160 struct fid_hash_node *fhn, *next;
162 list_for_each_entry_safe(fhn, next, list, fhn_node) {
164 fid_hash_add(new_head, new_shift, fhn);
181 /* Entry in the batching hash. */
183 struct fid_hash_node alre_fid_hash_node;
184 time_t alre_time[ALR_RW_MAX]; /* Not strictly needed. */
185 __u64 alre_begin[ALR_RW_MAX];
186 __u64 alre_end[ALR_RW_MAX];
187 __u64 alre_size[ALR_RW_MAX];
188 __u64 alre_segment_count[ALR_RW_MAX];
189 __u64 alre_count[ALR_RW_MAX];
190 char alre_obd_name[];
194 ALR_BATCH_HASH_SHIFT_DEFAULT = 10,
195 ALR_BATCH_HASH_SHIFT_MAX = 30,
199 struct list_head *alrb_hash;
200 unsigned int alrb_hash_shift;
201 unsigned int alrb_count;
204 static void alre_del_init(struct alr_entry *alre)
206 fhn_del_init(&alre->alre_fid_hash_node);
209 static void alre_update(struct alr_entry *alre, time_t time, __u64 begin,
210 __u64 end, __u32 size, __u32 segment_count, __u32 flags)
212 enum alr_rw d = (flags & OFD_ACCESS_READ) ? ALR_READ : ALR_WRITE;
214 alre->alre_time[d] = max_t(time_t, alre->alre_time[d], time);
215 alre->alre_begin[d] = min_t(__u64, alre->alre_begin[d], begin);
216 alre->alre_end[d] = max_t(__u64, alre->alre_end[d], end);
217 alre->alre_size[d] += size;
218 alre->alre_segment_count[d] += segment_count;
219 alre->alre_count[d] += 1;
222 int alr_batch_add(struct alr_batch *alrb, const char *obd_name,
223 const struct lu_fid *pfid, time_t time, __u64 begin, __u64 end,
224 __u32 size, __u32 segment_count, __u32 flags)
226 struct fid_hash_node fhn, *p;
227 struct alr_entry *alre;
233 assert(sizeof(time_t) == sizeof(__u64));
235 fhn_init(&fhn, pfid);
237 /* Find old or insert sentinel (fhn). Replace sentinel if returned. */
238 p = fid_hash_insert(alrb->alrb_hash, alrb->alrb_hash_shift, &fhn);
240 size_t alre_size = sizeof(*alre) + strlen(obd_name) + 1;
242 alre = calloc(1, alre_size);
248 fhn_init(&alre->alre_fid_hash_node, pfid);
249 strcpy(alre->alre_obd_name, obd_name);
250 fhn_replace_init(&fhn, &alre->alre_fid_hash_node);
253 alre = container_of(p, struct alr_entry, alre_fid_hash_node);
256 alre_update(alre, time, begin, end, size, segment_count, flags);
264 int sort_compare(const void *a1, const void *a2)
266 int l = *(const int*)a1;
267 int r = *(const int *)a2;
268 if (l > r) return -1;
273 static void alre_printf(FILE *f, struct alr_entry *alre, enum alr_rw d)
275 fprintf(f, "o=%s f="DFID" t=%lld b=%llu e=%llu s=%llu g=%llu n=%llu d=%c\n",
277 PFID(&alre->alre_fid_hash_node.fhn_fid),
278 (long long)alre->alre_time[d],
279 (unsigned long long)alre->alre_begin[d],
280 (unsigned long long)alre->alre_end[d],
281 (unsigned long long)alre->alre_size[d],
282 (unsigned long long)alre->alre_segment_count[d],
283 (unsigned long long)alre->alre_count[d],
284 (d == ALR_READ) ? 'r' : 'w');
287 struct alr_thread_arg {
288 struct list_head list;
291 pthread_mutex_t *file_mutex;
295 static void *alr_sort_and_print_thread(void *arg)
297 struct alr_entry *alre, *next;
298 struct alr_thread_arg *aa = arg;
299 struct list_head *tmp = &aa->list;
305 list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
306 if (alre->alre_count[ALR_READ] > 0)
308 if (alre->alre_count[ALR_WRITE] > 0)
315 sa = calloc(nr, sizeof(*sa));
317 fprintf(stderr, "cannot allocate memory for sorting\n");
322 list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
323 if (alre->alre_count[ALR_READ] > 0)
324 sa[i++] = alre->alre_count[ALR_READ];
325 if (alre->alre_count[ALR_WRITE] > 0)
326 sa[i++] = alre->alre_count[ALR_WRITE];
329 qsort(sa, nr, sizeof(*sa), sort_compare);
330 i = nr * aa->fraction / 100;
337 /* Prevent jumbled output from multiple concurrent sort and
339 rc = pthread_mutex_lock(aa->file_mutex);
341 fprintf(stderr, "cannot lock batch file: %s\n",
346 /* there might be lots of items at @cut, but we want to limit total
347 * output. so the first loop dumps all items > @cut and the second
348 * loop dumps items=@cut so that total number (@i) is not exceeeded.
349 * XXX: possible optimization - move items=@cut to another list, so
350 * that 2nd pass takes < O(n) */
351 list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
352 for (d = 0; d < ALR_RW_MAX; d++) {
353 if (alre->alre_count[d] <= cut)
355 alre_printf(aa->file, alre, d);
360 list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
361 for (d = 0; d < ALR_RW_MAX && i > 0; d++) {
362 if (alre->alre_count[d] != cut)
364 alre_printf(aa->file, alre, d);
369 rc = pthread_mutex_unlock(aa->file_mutex);
371 fprintf(stderr, "cannot unlock batch file: %s\n",
379 list_for_each_entry_safe(alre, next, tmp, alre_fid_hash_node.fhn_node) {
389 /* Fraction == 100 */
390 static void *alr_print_thread_fraction_100(void *arg)
392 struct alr_entry *alre, *next;
393 struct alr_thread_arg *aa = arg;
396 /* Prevent jumbled output from multiple concurrent sort and
398 rc = pthread_mutex_lock(aa->file_mutex);
400 fprintf(stderr, "cannot lock batch file: %s\n", strerror(rc));
404 list_for_each_entry(alre, &aa->list, alre_fid_hash_node.fhn_node) {
407 for (d = 0; d < ALR_RW_MAX; d++) {
408 if (alre->alre_count[d] != 0)
409 alre_printf(aa->file, alre, d);
413 rc = pthread_mutex_unlock(aa->file_mutex);
415 fprintf(stderr, "cannot unlock batch file: %s\n", strerror(rc));
421 list_for_each_entry_safe(alre, next, &aa->list, alre_fid_hash_node.fhn_node) {
431 /* Print, clear, and resize the batch. */
432 int alr_batch_print(struct alr_batch *alrb, FILE *file,
433 pthread_mutex_t *file_mutex, int fraction)
435 unsigned int new_hash_shift;
436 pthread_attr_t attr, *pattr = NULL;
437 struct alr_thread_arg *aa = NULL;
444 aa = calloc(1, sizeof(*aa));
448 /* move all collected items to the temp list */
449 INIT_LIST_HEAD(&aa->list);
450 for (i = 0; i < (1 << alrb->alrb_hash_shift); i++) {
451 if (list_empty(&alrb->alrb_hash[i]))
453 list_splice(&alrb->alrb_hash[i], &aa->list);
454 INIT_LIST_HEAD(&alrb->alrb_hash[i]);
457 aa->file_mutex = file_mutex;
458 aa->fraction = fraction;
460 rc = pthread_attr_init(&attr);
466 rc = pthread_attr_setdetachstate(pattr, PTHREAD_CREATE_DETACHED);
470 /* as sorting may take time and we don't want to lose access
471 * records we better do sorting and printing in a different thread */
473 if (fraction >= 100) /* Print all 100% records */
474 rc = pthread_create(&pid, pattr, &alr_print_thread_fraction_100, aa);
476 rc = pthread_create(&pid, pattr, &alr_sort_and_print_thread, aa);
480 aa = NULL; /* Sort and print thread owns it now. */
482 /* Resize hash based on previous count. */
483 new_hash_shift = alrb->alrb_hash_shift;
485 while (new_hash_shift < ALR_BATCH_HASH_SHIFT_MAX &&
486 (1 << new_hash_shift) < alrb->alrb_count)
489 fid_hash_resize(&alrb->alrb_hash, &alrb->alrb_hash_shift,
492 alrb->alrb_count = 0;
495 pthread_attr_destroy(pattr);
498 struct alr_entry *alre, *next;
500 list_for_each_entry_safe(alre, next, &aa->list,
501 alre_fid_hash_node.fhn_node) {
510 rc = -rc; /* Fixup pthread return conventions. */
515 struct alr_batch *alr_batch_create(unsigned int shift)
517 struct alr_batch *alrb;
521 shift = ALR_BATCH_HASH_SHIFT_DEFAULT;
523 alrb = calloc(1, sizeof(*alrb));
527 rc = fid_hash_init(&alrb->alrb_hash, &alrb->alrb_hash_shift, shift);
536 void alr_batch_destroy(struct alr_batch *alrb)
543 for (i = 0; i < (1 << alrb->alrb_hash_shift); i++) {
544 struct list_head *list = &alrb->alrb_hash[i];
545 struct alr_entry *alre, *next;
547 list_for_each_entry_safe(alre, next, list, alre_fid_hash_node.fhn_node) {
553 free(alrb->alrb_hash);