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);
180 /* Entry in the batching hash. */
182 struct fid_hash_node alre_fid_hash_node;
183 time_t alre_time[2]; /* Not strictly needed. */
187 __u64 alre_segment_count[2];
189 char alre_obd_name[];
193 ALR_BATCH_HASH_SHIFT_DEFAULT = 10,
194 ALR_BATCH_HASH_SHIFT_MAX = 30,
198 struct list_head *alrb_hash;
199 unsigned int alrb_hash_shift;
200 unsigned int alrb_count;
203 static void alre_del_init(struct alr_entry *alre)
205 fhn_del_init(&alre->alre_fid_hash_node);
208 static void alre_update(struct alr_entry *alre, time_t time, __u64 begin,
209 __u64 end, __u32 size, __u32 segment_count, __u32 flags)
211 unsigned int d = (flags & OFD_ACCESS_READ) ? ALR_READ : ALR_WRITE;
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;
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)
225 struct fid_hash_node fhn, *p;
226 struct alr_entry *alre;
232 assert(sizeof(time_t) == sizeof(__u64));
234 fhn_init(&fhn, pfid);
236 /* Find old or insert sentinel (fhn). Replace sentinel if returned. */
237 p = fid_hash_insert(alrb->alrb_hash, alrb->alrb_hash_shift, &fhn);
239 size_t alre_size = sizeof(*alre) + strlen(obd_name) + 1;
241 alre = calloc(1, alre_size);
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);
252 alre = container_of(p, struct alr_entry, alre_fid_hash_node);
255 alre_update(alre, time, begin, end, size, segment_count, flags);
263 int sort_compare(const void *a1, const void *a2)
265 int l = *(const int*)a1;
266 int r = *(const int *)a2;
267 if (l > r) return -1;
272 static void alre_printf(FILE *f, struct alr_entry *alre, int d)
274 fprintf(f, "o=%s f="DFID" t=%lld b=%llu e=%llu s=%llu g=%llu n=%llu d=%c\n",
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');
286 struct alr_thread_arg {
287 struct list_head list;
290 pthread_mutex_t *file_mutex;
293 void *alr_sort_and_print_thread(void *arg)
295 struct alr_entry *alre, *next;
296 struct alr_thread_arg *aa = arg;
297 struct list_head *tmp = &aa->list;
299 int rc, d, i, nr = 0;
302 list_for_each_entry(alre, tmp, alre_fid_hash_node.fhn_node) {
303 if (alre->alre_count[0] > 0)
305 if (alre->alre_count[1] > 0)
312 sa = calloc(nr, sizeof(*sa));
314 fprintf(stderr, "cannot allocate memory for sorting\n");
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];
326 qsort(sa, nr, sizeof(*sa), sort_compare);
327 i = nr * aa->fraction / 100;
333 /* Prevent jumbled output from multiple concurrent sort and
335 rc = pthread_mutex_lock(aa->file_mutex);
337 fprintf(stderr, "cannot lock batch file: %s\n",
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)
351 alre_printf(aa->file, alre, d);
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)
360 alre_printf(aa->file, alre, d);
365 rc = pthread_mutex_unlock(aa->file_mutex);
367 fprintf(stderr, "cannot unlock batch file: %s\n",
375 list_for_each_entry_safe(alre, next, tmp, alre_fid_hash_node.fhn_node) {
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)
389 unsigned int new_hash_shift;
390 pthread_attr_t attr, *pattr = NULL;
391 struct alr_thread_arg *aa = NULL;
398 aa = calloc(1, sizeof(*aa));
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]))
407 list_splice(&alrb->alrb_hash[i], &aa->list);
408 INIT_LIST_HEAD(&alrb->alrb_hash[i]);
411 aa->file_mutex = file_mutex;
412 aa->fraction = fraction;
414 rc = pthread_attr_init(&attr);
420 rc = pthread_attr_setdetachstate(pattr, PTHREAD_CREATE_DETACHED);
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);
430 aa = NULL; /* Sort and print thread owns it now. */
432 /* Resize hash based on previous count. */
433 new_hash_shift = alrb->alrb_hash_shift;
435 while (new_hash_shift < ALR_BATCH_HASH_SHIFT_MAX &&
436 (1 << new_hash_shift) < alrb->alrb_count)
439 fid_hash_resize(&alrb->alrb_hash, &alrb->alrb_hash_shift,
442 alrb->alrb_count = 0;
445 pthread_attr_destroy(pattr);
448 struct alr_entry *alre, *next;
450 list_for_each_entry_safe(alre, next, &aa->list,
451 alre_fid_hash_node.fhn_node) {
460 rc = -rc; /* Fixup pthread return conventions. */
465 struct alr_batch *alr_batch_create(unsigned int shift)
467 struct alr_batch *alrb;
471 shift = ALR_BATCH_HASH_SHIFT_DEFAULT;
473 alrb = calloc(1, sizeof(*alrb));
477 rc = fid_hash_init(&alrb->alrb_hash, &alrb->alrb_hash_shift, shift);
486 void alr_batch_destroy(struct alr_batch *alrb)
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;
497 list_for_each_entry_safe(alre, next, list, alre_fid_hash_node.fhn_node) {
503 free(alrb->alrb_hash);