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 static 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 static struct fid_hash_node *
92 fid_hash_insert(struct list_head *head, unsigned int shift, struct fid_hash_node *new_fhn)
94 struct list_head *list;
95 struct fid_hash_node *old_fhn, *next;
97 list = &head[llapi_fid_hash(&new_fhn->fhn_fid, shift)];
98 list_for_each_entry_safe(old_fhn, next, list, fhn_node) {
99 assert(fhn_is_hashed(old_fhn));
101 if (fid_eq(&old_fhn->fhn_fid, &new_fhn->fhn_fid))
105 list_add(&new_fhn->fhn_node, list);
110 static int fid_hash_init(struct list_head **phead, unsigned int *pshift,
113 struct list_head *new_head;
116 new_head = malloc(sizeof(*new_head) << shift);
117 if (new_head == NULL)
120 for (i = 0; i < (1 << shift); i++)
121 INIT_LIST_HEAD(&new_head[i]);
129 static int fid_hash_resize(struct list_head **phead, unsigned int *pshift,
130 unsigned int new_shift)
132 struct list_head *new_head;
136 if (*pshift == new_shift)
139 rc = fid_hash_init(&new_head, &new_shift, new_shift);
143 for (i = 0; i < (1 << *pshift); i++) {
144 struct list_head *list = &(*phead)[i];
145 struct fid_hash_node *fhn, *next;
147 list_for_each_entry_safe(fhn, next, list, fhn_node) {
149 fid_hash_add(new_head, new_shift, fhn);
166 /* Entry in the batching hash. */
168 struct fid_hash_node alre_fid_hash_node;
169 time_t alre_time[ALR_RW_MAX]; /* Not strictly needed. */
170 __u64 alre_begin[ALR_RW_MAX];
171 __u64 alre_end[ALR_RW_MAX];
172 __u64 alre_size[ALR_RW_MAX];
173 __u64 alre_segment_count[ALR_RW_MAX];
174 __u64 alre_count[ALR_RW_MAX];
175 char alre_obd_name[];
179 ALR_BATCH_HASH_SHIFT_DEFAULT = 10,
180 ALR_BATCH_HASH_SHIFT_MAX = 30,
184 struct list_head *alrb_hash;
185 unsigned int alrb_hash_shift;
186 unsigned int alrb_count;
189 static void alre_del_init(struct alr_entry *alre)
191 fhn_del_init(&alre->alre_fid_hash_node);
194 static void alre_update(struct alr_entry *alre, time_t time, __u64 begin,
195 __u64 end, __u32 size, __u32 segment_count, __u32 flags)
197 enum alr_rw d = (flags & OFD_ACCESS_READ) ? ALR_READ : ALR_WRITE;
199 alre->alre_time[d] = max_t(time_t, alre->alre_time[d], time);
200 alre->alre_begin[d] = min_t(__u64, alre->alre_begin[d], begin);
201 alre->alre_end[d] = max_t(__u64, alre->alre_end[d], end);
202 alre->alre_size[d] += size;
203 alre->alre_segment_count[d] += segment_count;
204 alre->alre_count[d] += 1;
207 int alr_batch_add(struct alr_batch *alrb, const char *obd_name,
208 const struct lu_fid *pfid, time_t time, __u64 begin, __u64 end,
209 __u32 size, __u32 segment_count, __u32 flags)
211 struct fid_hash_node fhn, *p;
212 struct alr_entry *alre;
218 assert(sizeof(time_t) == sizeof(__u64));
220 fhn_init(&fhn, pfid);
222 /* Find old or insert sentinel (fhn). Replace sentinel if returned. */
223 p = fid_hash_insert(alrb->alrb_hash, alrb->alrb_hash_shift, &fhn);
225 size_t alre_size = sizeof(*alre) + strlen(obd_name) + 1;
227 alre = calloc(1, alre_size);
233 fhn_init(&alre->alre_fid_hash_node, pfid);
234 strcpy(alre->alre_obd_name, obd_name);
235 fhn_replace_init(&fhn, &alre->alre_fid_hash_node);
238 alre = container_of(p, struct alr_entry, alre_fid_hash_node);
241 alre_update(alre, time, begin, end, size, segment_count, flags);
249 static int sort_compare(const void *a1, const void *a2)
251 int l = *(const int*)a1;
252 int r = *(const int *)a2;
253 if (l > r) return -1;
258 static void alre_printf(FILE *f, struct alr_entry *alre, enum alr_rw d)
260 fprintf(f, "o=%s f="DFID" t=%lld b=%llu e=%llu s=%llu g=%llu n=%llu d=%c\n",
262 PFID(&alre->alre_fid_hash_node.fhn_fid),
263 (long long)alre->alre_time[d],
264 (unsigned long long)alre->alre_begin[d],
265 (unsigned long long)alre->alre_end[d],
266 (unsigned long long)alre->alre_size[d],
267 (unsigned long long)alre->alre_segment_count[d],
268 (unsigned long long)alre->alre_count[d],
269 (d == ALR_READ) ? 'r' : 'w');
272 struct alr_thread_arg {
273 struct list_head list;
276 pthread_mutex_t *file_mutex;
280 static void *alr_sort_and_print_thread(void *arg)
282 struct alr_entry *alre, *next;
283 struct alr_thread_arg *aa = arg;
284 struct list_head *tmp = &aa->list;
290 list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
291 if (alre->alre_count[ALR_READ] > 0)
293 if (alre->alre_count[ALR_WRITE] > 0)
300 sa = calloc(nr, sizeof(*sa));
302 fprintf(stderr, "cannot allocate memory for sorting\n");
307 list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
308 if (alre->alre_count[ALR_READ] > 0)
309 sa[i++] = alre->alre_count[ALR_READ];
310 if (alre->alre_count[ALR_WRITE] > 0)
311 sa[i++] = alre->alre_count[ALR_WRITE];
314 qsort(sa, nr, sizeof(*sa), sort_compare);
315 i = nr * aa->fraction / 100;
322 /* Prevent jumbled output from multiple concurrent sort and
324 rc = pthread_mutex_lock(aa->file_mutex);
326 fprintf(stderr, "cannot lock batch file: %s\n",
331 /* there might be lots of items at @cut, but we want to limit total
332 * output. so the first loop dumps all items > @cut and the second
333 * loop dumps items=@cut so that total number (@i) is not exceeeded.
334 * XXX: possible optimization - move items=@cut to another list, so
335 * that 2nd pass takes < O(n) */
336 list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
337 for (d = 0; d < ALR_RW_MAX; d++) {
338 if (alre->alre_count[d] <= cut)
340 alre_printf(aa->file, alre, d);
345 list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
346 for (d = 0; d < ALR_RW_MAX && i > 0; d++) {
347 if (alre->alre_count[d] != cut)
349 alre_printf(aa->file, alre, d);
354 rc = pthread_mutex_unlock(aa->file_mutex);
356 fprintf(stderr, "cannot unlock batch file: %s\n",
364 list_for_each_entry_safe(alre, next, tmp, alre_fid_hash_node.fhn_node) {
374 /* Fraction == 100 */
375 static void *alr_print_thread_fraction_100(void *arg)
377 struct alr_entry *alre, *next;
378 struct alr_thread_arg *aa = arg;
381 /* Prevent jumbled output from multiple concurrent sort and
383 rc = pthread_mutex_lock(aa->file_mutex);
385 fprintf(stderr, "cannot lock batch file: %s\n", strerror(rc));
389 list_for_each_entry(alre, &aa->list, alre_fid_hash_node.fhn_node) {
392 for (d = 0; d < ALR_RW_MAX; d++) {
393 if (alre->alre_count[d] != 0)
394 alre_printf(aa->file, alre, d);
398 rc = pthread_mutex_unlock(aa->file_mutex);
400 fprintf(stderr, "cannot unlock batch file: %s\n", strerror(rc));
406 list_for_each_entry_safe(alre, next, &aa->list, alre_fid_hash_node.fhn_node) {
416 /* Print, clear, and resize the batch. */
417 int alr_batch_print(struct alr_batch *alrb, FILE *file,
418 pthread_mutex_t *file_mutex, int fraction)
420 unsigned int new_hash_shift;
421 pthread_attr_t attr, *pattr = NULL;
422 struct alr_thread_arg *aa = NULL;
429 aa = calloc(1, sizeof(*aa));
433 /* move all collected items to the temp list */
434 INIT_LIST_HEAD(&aa->list);
435 for (i = 0; i < (1 << alrb->alrb_hash_shift); i++) {
436 if (list_empty(&alrb->alrb_hash[i]))
438 list_splice(&alrb->alrb_hash[i], &aa->list);
439 INIT_LIST_HEAD(&alrb->alrb_hash[i]);
442 aa->file_mutex = file_mutex;
443 aa->fraction = fraction;
445 rc = pthread_attr_init(&attr);
451 rc = pthread_attr_setdetachstate(pattr, PTHREAD_CREATE_DETACHED);
455 /* as sorting may take time and we don't want to lose access
456 * records we better do sorting and printing in a different thread */
458 if (fraction >= 100) /* Print all 100% records */
459 rc = pthread_create(&pid, pattr, &alr_print_thread_fraction_100, aa);
461 rc = pthread_create(&pid, pattr, &alr_sort_and_print_thread, aa);
465 aa = NULL; /* Sort and print thread owns it now. */
467 /* Resize hash based on previous count. */
468 new_hash_shift = alrb->alrb_hash_shift;
470 while (new_hash_shift < ALR_BATCH_HASH_SHIFT_MAX &&
471 (1 << new_hash_shift) < alrb->alrb_count)
474 fid_hash_resize(&alrb->alrb_hash, &alrb->alrb_hash_shift,
477 alrb->alrb_count = 0;
480 pthread_attr_destroy(pattr);
483 struct alr_entry *alre, *next;
485 list_for_each_entry_safe(alre, next, &aa->list,
486 alre_fid_hash_node.fhn_node) {
495 rc = -rc; /* Fixup pthread return conventions. */
500 struct alr_batch *alr_batch_create(unsigned int shift)
502 struct alr_batch *alrb;
506 shift = ALR_BATCH_HASH_SHIFT_DEFAULT;
508 alrb = calloc(1, sizeof(*alrb));
512 rc = fid_hash_init(&alrb->alrb_hash, &alrb->alrb_hash_shift, shift);
521 void alr_batch_destroy(struct alr_batch *alrb)
528 for (i = 0; i < (1 << alrb->alrb_hash_shift); i++) {
529 struct list_head *list = &alrb->alrb_hash[i];
530 struct alr_entry *alre, *next;
532 list_for_each_entry_safe(alre, next, list, alre_fid_hash_node.fhn_node) {
538 free(alrb->alrb_hash);