1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2012 STRATO AG. All rights reserved.
  23  */
  24 #include <sys/zfs_context.h>
  25 #include <sys/errno.h>
  26 #include <sys/far_impl.h>
  27 
  28 static void
  29 far_count_value_free(mod_hash_val_t val)
  30 {
  31         far_count_elem_t *fce = val;
  32         kmem_free(fce, sizeof (*fce));
  33 }
  34 
  35 static void
  36 far_count_key_free(mod_hash_key_t key)
  37 {
  38         kmem_free(key, sizeof (uint64_t));
  39 }
  40 
  41 static int
  42 far_count_cmp(const void *aa, const void *bb)
  43 {
  44         const far_count_elem_t *a = aa;
  45         const far_count_elem_t *b = bb;
  46 
  47         if (a->fce_ino < b->fce_ino)
  48                 return -1;
  49         if (a->fce_ino > b->fce_ino)
  50                 return 1;
  51         return 0;
  52 }
  53 
  54 int
  55 far_add_count(far_counter_t *fc, uint64_t ino, uint64_t inc,
  56     uint64_t aux, uint64_t *new_count, uint64_t *old_aux)
  57 {
  58         far_count_elem_t *fce;
  59         far_count_elem_t e = { .fce_ino = ino };
  60         avl_index_t where;
  61 
  62         fce = avl_find(&fc->fc_avl, &e, &where);
  63 
  64         if (!fce) {
  65                 fce = kmem_alloc(sizeof (*fce), KM_SLEEP);
  66                 fce->fce_count = 0;
  67                 fce->fce_aux = 0;
  68                 fce->fce_ino = ino;
  69                 avl_insert(&fc->fc_avl, fce, where);
  70         }
  71 
  72         if (old_aux) {
  73                 *old_aux = fce->fce_aux;
  74                 fce->fce_aux = aux;
  75         }
  76         fce->fce_count += inc;
  77 
  78         if (new_count)
  79                 *new_count = fce->fce_count;
  80 
  81         return (0);
  82 }
  83 
  84 int
  85 far_get_count(far_counter_t *fc, uint64_t ino, uint64_t *count, uint64_t *aux)
  86 {
  87         far_count_elem_t *fce;
  88         far_count_elem_t e = { .fce_ino = ino };
  89 
  90         fce = avl_find(&fc->fc_avl, &e, NULL);
  91         if (!fce) {
  92                 if (count)
  93                         *count = 0;
  94                 if (aux)
  95                         *aux = 0;
  96                 return (ENOENT);
  97         } else {
  98                 if (count)
  99                         *count = fce->fce_count;
 100                 if (aux)
 101                         *aux = fce->fce_aux;
 102                 return (0);
 103         }
 104 }
 105 
 106 void
 107 far_free_count(far_counter_t *fc, uint64_t ino)
 108 {
 109         far_count_elem_t *fce;
 110         far_count_elem_t e = { .fce_ino = ino };
 111 
 112         fce = avl_find(&fc->fc_avl, &e, NULL);
 113         if (!fce)
 114                 return;
 115         avl_remove(&fc->fc_avl, fce);
 116         kmem_free(fce, sizeof (*fce));
 117 }
 118 
 119 int
 120 far_count_init(far_counter_t *fc, char *name)
 121 {
 122         avl_create(&fc->fc_avl, far_count_cmp, sizeof (far_count_elem_t),
 123             offsetof(far_count_elem_t, fce_avl_node));
 124         fc->fc_name = name;
 125 
 126         return 0;
 127 }
 128 
 129 int
 130 far_count_fini(far_counter_t *fc)
 131 {
 132         far_count_elem_t *fce;
 133         int ret = 0;
 134 
 135         while ((fce = avl_first(&fc->fc_avl))) {
 136                 /*
 137                  * a count of zero might be left over if a file had > 1 links
 138                  * and be replaced by a file with > 1 link. see test 041.034
 139                  */
 140                 if (fce->fce_count != 0) {
 141                         cmn_err(CE_NOTE, "far_assert_count_empty: %s ino %"
 142                             PRIu64 " count %" PRIu64"\n", fc->fc_name,
 143                             fce->fce_ino, fce->fce_count);
 144                         ++ret;
 145                 }
 146                 avl_remove(&fc->fc_avl, fce);
 147                 kmem_free(fce, sizeof (*fce));
 148         }
 149         avl_destroy(&fc->fc_avl);
 150 
 151         return (ret);
 152 }