LCOV - code coverage report
Current view: top level - journal - journald-rate-limit.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 0 99 0.0 %
Date: 2015-07-29 18:47:03 Functions: 0 8 0.0 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2011 Lennart Poettering
       7             : 
       8             :   systemd is free software; you can redistribute it and/or modify it
       9             :   under the terms of the GNU Lesser General Public License as published by
      10             :   the Free Software Foundation; either version 2.1 of the License, or
      11             :   (at your option) any later version.
      12             : 
      13             :   systemd is distributed in the hope that it will be useful, but
      14             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      16             :   Lesser General Public License for more details.
      17             : 
      18             :   You should have received a copy of the GNU Lesser General Public License
      19             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      20             : ***/
      21             : 
      22             : #include <string.h>
      23             : #include <errno.h>
      24             : 
      25             : #include "journald-rate-limit.h"
      26             : #include "list.h"
      27             : #include "util.h"
      28             : #include "hashmap.h"
      29             : #include "random-util.h"
      30             : 
      31             : #define POOLS_MAX 5
      32             : #define BUCKETS_MAX 127
      33             : #define GROUPS_MAX 2047
      34             : 
      35             : static const int priority_map[] = {
      36             :         [LOG_EMERG]   = 0,
      37             :         [LOG_ALERT]   = 0,
      38             :         [LOG_CRIT]    = 0,
      39             :         [LOG_ERR]     = 1,
      40             :         [LOG_WARNING] = 2,
      41             :         [LOG_NOTICE]  = 3,
      42             :         [LOG_INFO]    = 3,
      43             :         [LOG_DEBUG]   = 4
      44             : };
      45             : 
      46             : typedef struct JournalRateLimitPool JournalRateLimitPool;
      47             : typedef struct JournalRateLimitGroup JournalRateLimitGroup;
      48             : 
      49             : struct JournalRateLimitPool {
      50             :         usec_t begin;
      51             :         unsigned num;
      52             :         unsigned suppressed;
      53             : };
      54             : 
      55             : struct JournalRateLimitGroup {
      56             :         JournalRateLimit *parent;
      57             : 
      58             :         char *id;
      59             :         JournalRateLimitPool pools[POOLS_MAX];
      60             :         unsigned long hash;
      61             : 
      62             :         LIST_FIELDS(JournalRateLimitGroup, bucket);
      63             :         LIST_FIELDS(JournalRateLimitGroup, lru);
      64             : };
      65             : 
      66             : struct JournalRateLimit {
      67             :         usec_t interval;
      68             :         unsigned burst;
      69             : 
      70             :         JournalRateLimitGroup* buckets[BUCKETS_MAX];
      71             :         JournalRateLimitGroup *lru, *lru_tail;
      72             : 
      73             :         unsigned n_groups;
      74             : 
      75             :         uint8_t hash_key[16];
      76             : };
      77             : 
      78           0 : JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst) {
      79             :         JournalRateLimit *r;
      80             : 
      81           0 :         assert(interval > 0 || burst == 0);
      82             : 
      83           0 :         r = new0(JournalRateLimit, 1);
      84           0 :         if (!r)
      85           0 :                 return NULL;
      86             : 
      87           0 :         r->interval = interval;
      88           0 :         r->burst = burst;
      89             : 
      90           0 :         random_bytes(r->hash_key, sizeof(r->hash_key));
      91             : 
      92           0 :         return r;
      93             : }
      94             : 
      95           0 : static void journal_rate_limit_group_free(JournalRateLimitGroup *g) {
      96           0 :         assert(g);
      97             : 
      98           0 :         if (g->parent) {
      99           0 :                 assert(g->parent->n_groups > 0);
     100             : 
     101           0 :                 if (g->parent->lru_tail == g)
     102           0 :                         g->parent->lru_tail = g->lru_prev;
     103             : 
     104           0 :                 LIST_REMOVE(lru, g->parent->lru, g);
     105           0 :                 LIST_REMOVE(bucket, g->parent->buckets[g->hash % BUCKETS_MAX], g);
     106             : 
     107           0 :                 g->parent->n_groups --;
     108             :         }
     109             : 
     110           0 :         free(g->id);
     111           0 :         free(g);
     112           0 : }
     113             : 
     114           0 : void journal_rate_limit_free(JournalRateLimit *r) {
     115           0 :         assert(r);
     116             : 
     117           0 :         while (r->lru)
     118           0 :                 journal_rate_limit_group_free(r->lru);
     119             : 
     120           0 :         free(r);
     121           0 : }
     122             : 
     123           0 : _pure_ static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, usec_t ts) {
     124             :         unsigned i;
     125             : 
     126           0 :         assert(g);
     127             : 
     128           0 :         for (i = 0; i < POOLS_MAX; i++)
     129           0 :                 if (g->pools[i].begin + g->parent->interval >= ts)
     130           0 :                         return false;
     131             : 
     132           0 :         return true;
     133             : }
     134             : 
     135           0 : static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) {
     136           0 :         assert(r);
     137             : 
     138             :         /* Makes room for at least one new item, but drop all
     139             :          * expored items too. */
     140             : 
     141           0 :         while (r->n_groups >= GROUPS_MAX ||
     142           0 :                (r->lru_tail && journal_rate_limit_group_expired(r->lru_tail, ts)))
     143           0 :                 journal_rate_limit_group_free(r->lru_tail);
     144           0 : }
     145             : 
     146           0 : static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t ts) {
     147             :         JournalRateLimitGroup *g;
     148             : 
     149           0 :         assert(r);
     150           0 :         assert(id);
     151             : 
     152           0 :         g = new0(JournalRateLimitGroup, 1);
     153           0 :         if (!g)
     154           0 :                 return NULL;
     155             : 
     156           0 :         g->id = strdup(id);
     157           0 :         if (!g->id)
     158           0 :                 goto fail;
     159             : 
     160           0 :         g->hash = string_hash_func(g->id, r->hash_key);
     161             : 
     162           0 :         journal_rate_limit_vacuum(r, ts);
     163             : 
     164           0 :         LIST_PREPEND(bucket, r->buckets[g->hash % BUCKETS_MAX], g);
     165           0 :         LIST_PREPEND(lru, r->lru, g);
     166           0 :         if (!g->lru_next)
     167           0 :                 r->lru_tail = g;
     168           0 :         r->n_groups ++;
     169             : 
     170           0 :         g->parent = r;
     171           0 :         return g;
     172             : 
     173             : fail:
     174           0 :         journal_rate_limit_group_free(g);
     175           0 :         return NULL;
     176             : }
     177             : 
     178           0 : static unsigned burst_modulate(unsigned burst, uint64_t available) {
     179             :         unsigned k;
     180             : 
     181             :         /* Modulates the burst rate a bit with the amount of available
     182             :          * disk space */
     183             : 
     184           0 :         k = u64log2(available);
     185             : 
     186             :         /* 1MB */
     187           0 :         if (k <= 20)
     188           0 :                 return burst;
     189             : 
     190           0 :         burst = (burst * (k-20)) / 4;
     191             : 
     192             :         /*
     193             :          * Example:
     194             :          *
     195             :          *      <= 1MB = rate * 1
     196             :          *        16MB = rate * 2
     197             :          *       256MB = rate * 3
     198             :          *         4GB = rate * 4
     199             :          *        64GB = rate * 5
     200             :          *         1TB = rate * 6
     201             :          */
     202             : 
     203           0 :         return burst;
     204             : }
     205             : 
     206           0 : int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) {
     207             :         unsigned long h;
     208             :         JournalRateLimitGroup *g;
     209             :         JournalRateLimitPool *p;
     210             :         unsigned burst;
     211             :         usec_t ts;
     212             : 
     213           0 :         assert(id);
     214             : 
     215           0 :         if (!r)
     216           0 :                 return 1;
     217             : 
     218           0 :         if (r->interval == 0 || r->burst == 0)
     219           0 :                 return 1;
     220             : 
     221           0 :         burst = burst_modulate(r->burst, available);
     222             : 
     223           0 :         ts = now(CLOCK_MONOTONIC);
     224             : 
     225           0 :         h = string_hash_func(id, r->hash_key);
     226           0 :         g = r->buckets[h % BUCKETS_MAX];
     227             : 
     228           0 :         LIST_FOREACH(bucket, g, g)
     229           0 :                 if (streq(g->id, id))
     230           0 :                         break;
     231             : 
     232           0 :         if (!g) {
     233           0 :                 g = journal_rate_limit_group_new(r, id, ts);
     234           0 :                 if (!g)
     235           0 :                         return -ENOMEM;
     236             :         }
     237             : 
     238           0 :         p = &g->pools[priority_map[priority]];
     239             : 
     240           0 :         if (p->begin <= 0) {
     241           0 :                 p->suppressed = 0;
     242           0 :                 p->num = 1;
     243           0 :                 p->begin = ts;
     244           0 :                 return 1;
     245             :         }
     246             : 
     247           0 :         if (p->begin + r->interval < ts) {
     248             :                 unsigned s;
     249             : 
     250           0 :                 s = p->suppressed;
     251           0 :                 p->suppressed = 0;
     252           0 :                 p->num = 1;
     253           0 :                 p->begin = ts;
     254             : 
     255           0 :                 return 1 + s;
     256             :         }
     257             : 
     258           0 :         if (p->num <= burst) {
     259           0 :                 p->num++;
     260           0 :                 return 1;
     261             :         }
     262             : 
     263           0 :         p->suppressed++;
     264           0 :         return 0;
     265             : }

Generated by: LCOV version 1.11