Annotation of embedaddon/bmon/src/attr.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * attr.c              Attributes
        !             3:  *
        !             4:  * Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
        !             5:  * Copyright (c) 2013 Red Hat, Inc.
        !             6:  *
        !             7:  * Permission is hereby granted, free of charge, to any person obtaining a
        !             8:  * copy of this software and associated documentation files (the "Software"),
        !             9:  * to deal in the Software without restriction, including without limitation
        !            10:  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
        !            11:  * and/or sell copies of the Software, and to permit persons to whom the
        !            12:  * Software is furnished to do so, subject to the following conditions:
        !            13:  *
        !            14:  * The above copyright notice and this permission notice shall be included
        !            15:  * in all copies or substantial portions of the Software.
        !            16:  *
        !            17:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
        !            18:  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        !            19:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
        !            20:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        !            21:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
        !            22:  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
        !            23:  * DEALINGS IN THE SOFTWARE.
        !            24:  */
        !            25: 
        !            26: #include <bmon/bmon.h>
        !            27: #include <bmon/conf.h>
        !            28: #include <bmon/attr.h>
        !            29: #include <bmon/history.h>
        !            30: #include <bmon/element.h>
        !            31: #include <bmon/unit.h>
        !            32: #include <bmon/input.h>
        !            33: #include <bmon/utils.h>
        !            34: 
        !            35: #if 0
        !            36: 
        !            37: #define MAX_POLICY             255
        !            38: 
        !            39: static char * allowed_attrs[MAX_POLICY];
        !            40: static char * denied_attrs[MAX_POLICY];
        !            41: static int allow_all_attrs;
        !            42: 
        !            43: #endif
        !            44: 
        !            45: static LIST_HEAD(attr_def_list);
        !            46: static int attr_id_gen = 1;
        !            47: 
        !            48: struct attr_def *attr_def_lookup(const char *name)
        !            49: {
        !            50:        struct attr_def *def;
        !            51: 
        !            52:        list_for_each_entry(def, &attr_def_list, ad_list)
        !            53:                if (!strcmp(name, def->ad_name))
        !            54:                        return def;
        !            55: 
        !            56:        return NULL;
        !            57: }
        !            58: 
        !            59: struct attr_def *attr_def_lookup_id(int id)
        !            60: {
        !            61:        struct attr_def *def;
        !            62: 
        !            63:        list_for_each_entry(def, &attr_def_list, ad_list)
        !            64:                if (def->ad_id == id)
        !            65:                        return def;
        !            66: 
        !            67:        return NULL;
        !            68: }
        !            69: 
        !            70: #if 0
        !            71: int foreach_attr_type(int (*cb)(struct attr_type *, void *), void *arg)
        !            72: {
        !            73:        int i, err = 0;
        !            74: 
        !            75:        for (i = 0; i < NAME_HASHSIZ; i++) {
        !            76:                struct attr_type *t;
        !            77:                for (t = attr_ht_name[i]; t; t = t->at_next_name) {
        !            78:                        err = cb(t, arg);
        !            79:                        if (err < 0)
        !            80:                                break;
        !            81:                }
        !            82:        }
        !            83: 
        !            84:        return err;
        !            85: }
        !            86: #endif
        !            87: 
        !            88: int attr_def_add(const char *name, const char *desc, struct unit *unit,
        !            89:                 int type, int flags)
        !            90: {
        !            91:        struct attr_def *def;
        !            92: 
        !            93:        if ((def = attr_def_lookup(name)))
        !            94:                return def->ad_id;
        !            95: 
        !            96:        def = xcalloc(1, sizeof(*def));
        !            97: 
        !            98:        def->ad_id = attr_id_gen++;
        !            99:        def->ad_name = strdup(name);
        !           100: 
        !           101:        def->ad_description = strdup(desc ? : "");
        !           102:        def->ad_type = type;
        !           103:        def->ad_unit = unit;
        !           104:        def->ad_flags = flags;
        !           105: 
        !           106:        list_add_tail(&def->ad_list, &attr_def_list);
        !           107: 
        !           108:        DBG("New attribute %s desc=\"%s\" unit=%s type=%d",
        !           109:            def->ad_name, def->ad_description, def->ad_unit->u_name, type);
        !           110: 
        !           111:        return def->ad_id;
        !           112: }
        !           113: 
        !           114: void attr_def_free(struct attr_def *def)
        !           115: {
        !           116:        if (!def)
        !           117:                return;
        !           118: 
        !           119:        xfree(def->ad_name);
        !           120:        xfree(def->ad_description);
        !           121:        xfree(def);
        !           122: }
        !           123: 
        !           124: int attr_map_load(struct attr_map *map, size_t size)
        !           125: {
        !           126:        int i, nfailed = 0;
        !           127: 
        !           128:        for (i = 0; i < size; i++) {
        !           129:                struct attr_map *m = &map[i];
        !           130:                struct unit *u;
        !           131: 
        !           132:                if (!(u = unit_lookup(m->unit))) {
        !           133:                        nfailed++;
        !           134:                        continue;
        !           135:                }
        !           136: 
        !           137:                m->attrid = attr_def_add(m->name, m->description, u,
        !           138:                                         m->type, m->flags);
        !           139:        }
        !           140: 
        !           141:        return nfailed;
        !           142: }
        !           143: 
        !           144: static inline unsigned int attr_hash(int id)
        !           145: {
        !           146:        return id % ATTR_HASH_SIZE;
        !           147: }
        !           148: 
        !           149: struct attr *attr_lookup(const struct element *e, int id)
        !           150: {
        !           151:        unsigned int hash = attr_hash(id);
        !           152:        struct attr *attr;
        !           153: 
        !           154:        list_for_each_entry(attr, &e->e_attrhash[hash], a_list)
        !           155:                if (attr->a_def->ad_id == id)
        !           156:                        return attr;
        !           157: 
        !           158:        return NULL;
        !           159: }
        !           160: 
        !           161: static int collect_history(struct element *e, struct attr_def *def)
        !           162: {
        !           163:        int n;
        !           164: 
        !           165:        if (def->ad_flags & ATTR_FORCE_HISTORY)
        !           166:                return 1;
        !           167: 
        !           168:        for (n = 0; n <= GT_MAX; n++)
        !           169:                if (e->e_key_attr[n] == def)
        !           170:                        return 1;
        !           171: 
        !           172:        return 0;
        !           173: #if 0
        !           174:        if (!allowed_attrs[0] && !denied_attrs[0]) {
        !           175:                if (!strcmp(ad->ad_name, "bytes") ||
        !           176:                    !strcmp(ad->ad_name, "packets"))
        !           177:                        return 1;
        !           178:                else
        !           179:                        return 0;
        !           180:        }
        !           181: 
        !           182:        if (!allowed_attrs[0]) {
        !           183:                for (n = 0; n < MAX_POLICY && denied_attrs[n]; n++)
        !           184:                        if (!strcasecmp(denied_attrs[n], ad->ad_name))
        !           185:                                return 0;
        !           186:                return 1;
        !           187:        }
        !           188: 
        !           189:        for (n = 0; n < MAX_POLICY && denied_attrs[n]; n++)
        !           190:                if (!strcasecmp(denied_attrs[n], ad->ad_name))
        !           191:                        return 0;
        !           192:        
        !           193:        for (n=0; n < MAX_POLICY && allowed_attrs[n]; n++)
        !           194:                if (!strcasecmp(allowed_attrs[n], ad->ad_name))
        !           195:                        return 1;
        !           196: #endif
        !           197: }
        !           198: 
        !           199: #if 0
        !           200: 
        !           201: void attr_parse_policy(const char *policy)
        !           202: {
        !           203:        static int set = 0;
        !           204:        int i, a = 0, d = 0, f = 0;
        !           205:        char *p, *s;
        !           206: 
        !           207:        if (set)
        !           208:                return;
        !           209:        set = 1;
        !           210: 
        !           211:        if (!strcasecmp(policy, "all")) {
        !           212:                allow_all_attrs = 1;
        !           213:                return ;
        !           214:        }
        !           215: 
        !           216:        s = strdup(policy);
        !           217:        
        !           218:        for (i = 0, p = s; ; i++) {
        !           219:                if (s[i] == ',' || s[i] == '\0') {
        !           220: 
        !           221:                        f = s[i] == '\0' ? 1 : 0;
        !           222:                        s[i] = '\0';
        !           223:                        
        !           224:                        if ('!' == *p) {
        !           225:                                if (d > (MAX_POLICY - 1))
        !           226:                                        break;
        !           227:                                denied_attrs[d++] = strdup(++p);
        !           228:                        } else {
        !           229:                                if(a > (MAX_POLICY - 1))
        !           230:                                        break;
        !           231:                                allowed_attrs[a++] = strdup(p);
        !           232:                        }
        !           233:                        
        !           234:                        if (f)
        !           235:                                break;
        !           236:                        
        !           237:                        p = &s[i+1];
        !           238:                }
        !           239:        }
        !           240:        
        !           241:        xfree(s);
        !           242: }
        !           243: 
        !           244: #endif
        !           245: 
        !           246: void attr_start_collecting_history(struct attr *attr)
        !           247: {
        !           248:        if (attr->a_flags & ATTR_DOING_HISTORY)
        !           249:                return;
        !           250: 
        !           251:        DBG("Starting to collect history for attribute %s",
        !           252:            attr->a_def->ad_name);
        !           253: 
        !           254:        history_attach(attr);
        !           255:        attr->a_flags |= ATTR_DOING_HISTORY;
        !           256: }
        !           257: 
        !           258: static int attrcmp(struct element *e, struct attr *a, struct attr *b)
        !           259: {
        !           260:        /* major key attribute is always first */
        !           261:        if (e->e_key_attr[GT_MAJOR] == b->a_def)
        !           262:                return 1;
        !           263: 
        !           264:        /* minor key attribte is always second */
        !           265:        if (e->e_key_attr[GT_MINOR] == b->a_def)
        !           266:                return (e->e_key_attr[GT_MAJOR] == a->a_def) ? -1 : 1;
        !           267: 
        !           268:        /* otherwise sort by alphabet */
        !           269:        return strcasecmp(a->a_def->ad_description, b->a_def->ad_description);
        !           270: }
        !           271: 
        !           272: void attr_update(struct element *e, int id, uint64_t rx, uint64_t tx, int flags)
        !           273: {
        !           274:        struct attr *attr, *n;
        !           275:        int update_ts = 0;
        !           276: 
        !           277:        if (!(attr = attr_lookup(e, id))) {
        !           278:                unsigned int hash = attr_hash(id);
        !           279:                struct attr_def *def;
        !           280: 
        !           281:                if (!(def = attr_def_lookup_id(id)))
        !           282:                        return;
        !           283: 
        !           284:                DBG("Tracking new attribute %d (\"%s\") of element %s",
        !           285:                    def->ad_id, def->ad_name, e->e_name);
        !           286: 
        !           287:                attr = xcalloc(1, sizeof(*attr));
        !           288:                attr->a_def = def;
        !           289:                attr->a_flags = def->ad_flags;
        !           290: 
        !           291:                init_list_head(&attr->a_history_list);
        !           292: 
        !           293:                if (collect_history(e, def))
        !           294:                        attr_start_collecting_history(attr);
        !           295: 
        !           296:                list_add_tail(&attr->a_list, &e->e_attrhash[hash]);
        !           297:                e->e_nattrs++;
        !           298: 
        !           299:                list_for_each_entry(n, &e->e_attr_sorted, a_sort_list) {
        !           300:                        if (attrcmp(e, attr, n) < 0) {
        !           301:                                list_add_tail(&attr->a_sort_list,
        !           302:                                              &n->a_sort_list);
        !           303:                                goto inserted;
        !           304:                        }
        !           305:                }
        !           306: 
        !           307:                list_add_tail(&attr->a_sort_list, &e->e_attr_sorted);
        !           308:        }
        !           309: 
        !           310: inserted:
        !           311:        if (flags & UPDATE_FLAG_RX) {
        !           312:                attr->a_rx_rate.r_current = rx;
        !           313:                attr->a_flags |= ATTR_RX_ENABLED;
        !           314:                update_ts = 1;
        !           315:        }
        !           316: 
        !           317:        if (flags & UPDATE_FLAG_TX) {
        !           318:                attr->a_tx_rate.r_current = tx;
        !           319:                attr->a_flags |= ATTR_TX_ENABLED;
        !           320:                update_ts = 1;
        !           321:        }
        !           322: 
        !           323:        if (update_ts)
        !           324:                update_timestamp(&attr->a_last_update);
        !           325: 
        !           326:        DBG("Updated attribute %d (\"%s\") of element %s", id, attr->a_def->ad_name, e->e_name);
        !           327: }
        !           328: 
        !           329: void attr_free(struct attr *a)
        !           330: {
        !           331:        struct history *h, *n;
        !           332: 
        !           333:        list_for_each_entry_safe(h, n, &a->a_history_list, h_list)
        !           334:                history_free(h);
        !           335: 
        !           336:        list_del(&a->a_list);
        !           337: 
        !           338:        xfree(a);
        !           339: }
        !           340: 
        !           341: void attr_rate2float(struct attr *a, double *rx, char **rxu, int *rxprec,
        !           342:                                     double *tx, char **txu, int *txprec)
        !           343: {
        !           344:        struct unit *u = a->a_def->ad_unit;
        !           345: 
        !           346:        *rx = unit_value2str(a->a_rx_rate.r_rate, u, rxu, rxprec);
        !           347:        *tx = unit_value2str(a->a_tx_rate.r_rate, u, txu, txprec);
        !           348: }
        !           349: 
        !           350: struct attr *attr_select_first(void)
        !           351: {
        !           352:        struct element *e;
        !           353: 
        !           354:        if (!(e = element_current()))
        !           355:                return NULL;
        !           356: 
        !           357:        if (list_empty(&e->e_attr_sorted))
        !           358:                e->e_current_attr = NULL;
        !           359:        else
        !           360:                e->e_current_attr = list_first_entry(&e->e_attr_sorted,
        !           361:                                        struct attr, a_sort_list);
        !           362: 
        !           363:        return e->e_current_attr;
        !           364: }
        !           365: 
        !           366: struct attr *attr_select_last(void)
        !           367: {
        !           368:        struct element *e;
        !           369: 
        !           370:        if (!(e = element_current()))
        !           371:                return NULL;
        !           372: 
        !           373:        if (list_empty(&e->e_attr_sorted))
        !           374:                e->e_current_attr = NULL;
        !           375:        else
        !           376:                e->e_current_attr = list_entry(&e->e_attr_sorted,
        !           377:                                           struct attr, a_sort_list);
        !           378: 
        !           379:        return e->e_current_attr;
        !           380: }
        !           381: 
        !           382: struct attr *attr_select_next(void)
        !           383: {
        !           384:        struct element *e;
        !           385:        struct attr *a;
        !           386: 
        !           387:        if (!(e = element_current()))
        !           388:                return NULL;
        !           389: 
        !           390:        if (!(a = e->e_current_attr))
        !           391:                return attr_select_first();
        !           392: 
        !           393:        if (a->a_sort_list.next != &e->e_attr_sorted)
        !           394:                e->e_current_attr = list_entry(a->a_sort_list.next,
        !           395:                                           struct attr, a_sort_list);
        !           396:        else
        !           397:                return attr_select_first();
        !           398: 
        !           399:        return e->e_current_attr;
        !           400: }
        !           401: 
        !           402: struct attr *attr_select_prev(void)
        !           403: {
        !           404:        struct element *e;
        !           405:        struct attr *a;
        !           406: 
        !           407:        if (!(e = element_current()))
        !           408:                return NULL;
        !           409: 
        !           410:        if (!(a = e->e_current_attr))
        !           411:                return attr_select_last();
        !           412: 
        !           413:        if (a->a_sort_list.prev != &e->e_attr_sorted)
        !           414:                e->e_current_attr = list_entry(a->a_sort_list.prev,
        !           415:                                           struct attr, a_sort_list);
        !           416:        else
        !           417:                return attr_select_last();
        !           418: 
        !           419:        return e->e_current_attr;
        !           420: 
        !           421: }
        !           422: 
        !           423: struct attr *attr_current(void)
        !           424: {
        !           425:        struct element *e;
        !           426: 
        !           427:        if (!(e = element_current()))
        !           428:                return NULL;
        !           429: 
        !           430:        if (!e->e_current_attr)
        !           431:                return attr_select_first();
        !           432: 
        !           433:        return e->e_current_attr;
        !           434: }
        !           435: 
        !           436: #if 0
        !           437: int __first_attr(struct item *item, int graph)
        !           438: {
        !           439:        int i;
        !           440:        struct attr *a;
        !           441: 
        !           442:        for (i = 0; i < ATTR_HASH_MAX; i++) {
        !           443:                for (a = item->i_attrs[i]; a; a = a->a_next) {
        !           444:                        if (a->a_flags & ATTR_FLAG_HISTORY) {
        !           445:                                item->i_attr_sel[graph] = a;
        !           446:                                return 0;
        !           447:                        }
        !           448:                }
        !           449:        }
        !           450: 
        !           451:        return EMPTY_LIST;
        !           452: }
        !           453: 
        !           454: int __next_attr(struct item *item, int graph)
        !           455: {
        !           456:        int hash;
        !           457:        struct attr *attr, *next;
        !           458: 
        !           459:        if (item->i_attr_sel[graph] == NULL)
        !           460:                return __first_attr(item, graph);
        !           461: 
        !           462:        attr = item->i_attr_sel[graph];
        !           463:        hash = attr_hash(attr->a_def->ad_id);
        !           464:        next = attr->a_next;
        !           465: 
        !           466:        if (next == NULL)
        !           467:                hash++;
        !           468: 
        !           469:        for (; hash < ATTR_HASH_MAX; hash++) {
        !           470:                if (next) {
        !           471:                        attr = next;
        !           472:                        next = NULL;
        !           473:                } else
        !           474:                        attr = item->i_attrs[hash];
        !           475: 
        !           476:                for (; attr; attr = attr->a_next) {
        !           477:                        if (!(attr->a_flags & ATTR_FLAG_HISTORY))
        !           478:                                continue;
        !           479:                        item->i_attr_sel[graph] = attr;
        !           480:                        return 0;
        !           481:                }
        !           482:        }
        !           483: 
        !           484:        return __first_attr(item, graph);
        !           485: }
        !           486: 
        !           487: struct attr *attr_current(struct item *item, int graph)
        !           488: {
        !           489:        if (item->i_attr_sel[graph] == NULL)
        !           490:                __first_attr(item, graph);
        !           491: 
        !           492:        return item->i_attr_sel[graph];
        !           493: }
        !           494: 
        !           495: int attr_first(void)
        !           496: {
        !           497:        struct item *item = item_current();
        !           498:        
        !           499:        if (item == NULL)
        !           500:                return EMPTY_LIST;
        !           501: 
        !           502:        return __first_attr(item, item->i_graph_sel);
        !           503: }
        !           504: 
        !           505: int attr_next(void)
        !           506: {
        !           507:        struct item *item = item_current();
        !           508:        
        !           509:        if (item == NULL)
        !           510:                return EMPTY_LIST;
        !           511: 
        !           512:        return __next_attr(item, item->i_graph_sel);
        !           513: }
        !           514: #endif
        !           515: 
        !           516: static float __calc_usage(double rate, uint64_t max)
        !           517: {
        !           518:        if (!max)
        !           519:                return FLT_MAX;
        !           520: 
        !           521:        if (!rate)
        !           522:                return 0.0f;
        !           523: 
        !           524:        return 100.0f / ((double) max / (rate * cfg_rate_interval));
        !           525: }
        !           526: 
        !           527: void attr_calc_usage(struct attr *a, float *rx, float *tx,
        !           528:                     uint64_t rxmax, uint64_t txmax)
        !           529: {
        !           530:        if (a->a_def->ad_type == ATTR_TYPE_PERCENT) {
        !           531:                *rx = a->a_rx_rate.r_total;
        !           532:                *tx = a->a_tx_rate.r_total;
        !           533:        } else {
        !           534:                *rx = __calc_usage(a->a_rx_rate.r_rate, rxmax);
        !           535:                *tx = __calc_usage(a->a_tx_rate.r_rate, txmax);
        !           536:        }
        !           537: }
        !           538: 
        !           539: static void calc_counter_rate(struct attr *a, struct rate *rate,
        !           540:                              timestamp_t *ts)
        !           541: {
        !           542:        uint64_t delta, prev_total;
        !           543:        float diff, old_rate;
        !           544: 
        !           545:        if (rate->r_current < rate->r_prev) {
        !           546:                /* Overflow detected */
        !           547:                if (a->a_flags & ATTR_IGNORE_OVERFLOWS)
        !           548:                        delta = rate->r_current;
        !           549:                else {
        !           550:                        if (a->a_flags & ATTR_TRUE_64BIT)
        !           551:                                delta = 0xFFFFFFFFFFFFFFFFULL - rate->r_prev;
        !           552:                        else
        !           553:                                delta = 0xFFFFFFFFULL - rate->r_prev;
        !           554: 
        !           555:                        delta += rate->r_current + 1;
        !           556:                }
        !           557:        } else
        !           558:                delta = rate->r_current - rate->r_prev;
        !           559: 
        !           560:        prev_total = rate->r_total;
        !           561:        rate->r_total += delta;
        !           562:        rate->r_prev = rate->r_current;
        !           563: 
        !           564:        if (!prev_total) {
        !           565:                /*
        !           566:                 * No previous records exists, reset time to now to
        !           567:                 * avoid doing unnecessary calculation, this behaviour
        !           568:                 * continues as long as the counter stays 0.
        !           569:                 */
        !           570:                goto out;
        !           571:        }
        !           572:        
        !           573:        diff = timestamp_diff(&rate->r_last_calc, ts);
        !           574:        if (diff < (cfg_rate_interval - cfg_rate_variance))
        !           575:                return;
        !           576: 
        !           577:        old_rate = rate->r_rate;
        !           578: 
        !           579:        if (rate->r_total < prev_total) {
        !           580:                /* overflow */
        !           581:                delta = 0xFFFFFFFFFFFFFFFFULL - prev_total;
        !           582:                delta += rate->r_total + 1;
        !           583:        } else
        !           584:                delta = rate->r_total - prev_total;
        !           585: 
        !           586:        rate->r_rate = delta / diff;
        !           587: 
        !           588:        if (old_rate)
        !           589:                rate->r_rate = ((rate->r_rate * 3.0f) + old_rate) / 4.0f;
        !           590: 
        !           591: out:
        !           592:        copy_timestamp(&rate->r_last_calc, ts);
        !           593: }
        !           594: 
        !           595: static void calc_rate_total(struct attr *a, struct rate *rate, timestamp_t *ts)
        !           596: {
        !           597:        rate->r_prev = rate->r_rate = rate->r_total = rate->r_current;
        !           598:        copy_timestamp(&rate->r_last_calc, ts);
        !           599: }
        !           600: 
        !           601: void attr_notify_update(struct attr *a, timestamp_t *ts)
        !           602: {
        !           603:        switch (a->a_def->ad_type) {
        !           604:        case ATTR_TYPE_RATE:
        !           605:        case ATTR_TYPE_PERCENT:
        !           606:                calc_rate_total(a, &a->a_rx_rate, ts);
        !           607:                calc_rate_total(a, &a->a_tx_rate, ts);
        !           608:                break;
        !           609: 
        !           610:        case ATTR_TYPE_COUNTER:
        !           611:                calc_counter_rate(a, &a->a_rx_rate, ts);
        !           612:                calc_counter_rate(a, &a->a_tx_rate, ts);
        !           613:                break;
        !           614:        default:
        !           615:                DBG("Attribute update of unknown type");
        !           616:                break;
        !           617:        }
        !           618: 
        !           619:        if (a->a_flags & ATTR_DOING_HISTORY) {
        !           620:                struct history *h;
        !           621: 
        !           622:                DBG("Updating history of attribute %d (\"%s\")", a->a_def->ad_id, a->a_def->ad_name);
        !           623: 
        !           624:                list_for_each_entry(h, &a->a_history_list, h_list)
        !           625:                        history_update(a, h, ts);
        !           626:        }
        !           627: }
        !           628: 
        !           629: static void __exit attr_exit(void)
        !           630: {
        !           631:        struct attr_def *ad, *n;
        !           632: 
        !           633:        list_for_each_entry_safe(ad, n, &attr_def_list, ad_list)
        !           634:                attr_def_free(ad);
        !           635: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>