linux/drivers/staging/lustre/lustre/fld/fld_cache.c
<<
>>
Prefs
   1/*
   2 * GPL HEADER START
   3 *
   4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5 *
   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.
   9 *
  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).
  15 *
  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.sun.com/software/products/lustre/docs/GPLv2.pdf
  19 *
  20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  21 * CA 95054 USA or visit www.sun.com if you need additional information or
  22 * have any questions.
  23 *
  24 * GPL HEADER END
  25 */
  26/*
  27 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  28 * Use is subject to license terms.
  29 *
  30 * Copyright (c) 2012, 2013, Intel Corporation.
  31 */
  32/*
  33 * This file is part of Lustre, http://www.lustre.org/
  34 * Lustre is a trademark of Sun Microsystems, Inc.
  35 *
  36 * lustre/fld/fld_cache.c
  37 *
  38 * FLD (Fids Location Database)
  39 *
  40 * Author: Pravin Shelar <pravin.shelar@sun.com>
  41 * Author: Yury Umanets <umka@clusterfs.com>
  42 */
  43
  44#define DEBUG_SUBSYSTEM S_FLD
  45
  46# include <linux/libcfs/libcfs.h>
  47# include <linux/module.h>
  48# include <linux/jbd.h>
  49# include <asm/div64.h>
  50
  51#include <obd.h>
  52#include <obd_class.h>
  53#include <lustre_ver.h>
  54#include <obd_support.h>
  55#include <lprocfs_status.h>
  56
  57#include <dt_object.h>
  58#include <md_object.h>
  59#include <lustre_req_layout.h>
  60#include <lustre_fld.h>
  61#include "fld_internal.h"
  62
  63/**
  64 * create fld cache.
  65 */
  66struct fld_cache *fld_cache_init(const char *name,
  67                                 int cache_size, int cache_threshold)
  68{
  69        struct fld_cache *cache;
  70        ENTRY;
  71
  72        LASSERT(name != NULL);
  73        LASSERT(cache_threshold < cache_size);
  74
  75        OBD_ALLOC_PTR(cache);
  76        if (cache == NULL)
  77                RETURN(ERR_PTR(-ENOMEM));
  78
  79        INIT_LIST_HEAD(&cache->fci_entries_head);
  80        INIT_LIST_HEAD(&cache->fci_lru);
  81
  82        cache->fci_cache_count = 0;
  83        rwlock_init(&cache->fci_lock);
  84
  85        strlcpy(cache->fci_name, name,
  86                sizeof(cache->fci_name));
  87
  88        cache->fci_cache_size = cache_size;
  89        cache->fci_threshold = cache_threshold;
  90
  91        /* Init fld cache info. */
  92        memset(&cache->fci_stat, 0, sizeof(cache->fci_stat));
  93
  94        CDEBUG(D_INFO, "%s: FLD cache - Size: %d, Threshold: %d\n",
  95               cache->fci_name, cache_size, cache_threshold);
  96
  97        RETURN(cache);
  98}
  99
 100/**
 101 * destroy fld cache.
 102 */
 103void fld_cache_fini(struct fld_cache *cache)
 104{
 105        __u64 pct;
 106        ENTRY;
 107
 108        LASSERT(cache != NULL);
 109        fld_cache_flush(cache);
 110
 111        if (cache->fci_stat.fst_count > 0) {
 112                pct = cache->fci_stat.fst_cache * 100;
 113                do_div(pct, cache->fci_stat.fst_count);
 114        } else {
 115                pct = 0;
 116        }
 117
 118        CDEBUG(D_INFO, "FLD cache statistics (%s):\n", cache->fci_name);
 119        CDEBUG(D_INFO, "  Total reqs: "LPU64"\n", cache->fci_stat.fst_count);
 120        CDEBUG(D_INFO, "  Cache reqs: "LPU64"\n", cache->fci_stat.fst_cache);
 121        CDEBUG(D_INFO, "  Cache hits: "LPU64"%%\n", pct);
 122
 123        OBD_FREE_PTR(cache);
 124
 125        EXIT;
 126}
 127
 128/**
 129 * delete given node from list.
 130 */
 131void fld_cache_entry_delete(struct fld_cache *cache,
 132                            struct fld_cache_entry *node)
 133{
 134        list_del(&node->fce_list);
 135        list_del(&node->fce_lru);
 136        cache->fci_cache_count--;
 137        OBD_FREE_PTR(node);
 138}
 139
 140/**
 141 * fix list by checking new entry with NEXT entry in order.
 142 */
 143static void fld_fix_new_list(struct fld_cache *cache)
 144{
 145        struct fld_cache_entry *f_curr;
 146        struct fld_cache_entry *f_next;
 147        struct lu_seq_range *c_range;
 148        struct lu_seq_range *n_range;
 149        struct list_head *head = &cache->fci_entries_head;
 150        ENTRY;
 151
 152restart_fixup:
 153
 154        list_for_each_entry_safe(f_curr, f_next, head, fce_list) {
 155                c_range = &f_curr->fce_range;
 156                n_range = &f_next->fce_range;
 157
 158                LASSERT(range_is_sane(c_range));
 159                if (&f_next->fce_list == head)
 160                        break;
 161
 162                if (c_range->lsr_flags != n_range->lsr_flags)
 163                        continue;
 164
 165                LASSERTF(c_range->lsr_start <= n_range->lsr_start,
 166                         "cur lsr_start "DRANGE" next lsr_start "DRANGE"\n",
 167                         PRANGE(c_range), PRANGE(n_range));
 168
 169                /* check merge possibility with next range */
 170                if (c_range->lsr_end == n_range->lsr_start) {
 171                        if (c_range->lsr_index != n_range->lsr_index)
 172                                continue;
 173                        n_range->lsr_start = c_range->lsr_start;
 174                        fld_cache_entry_delete(cache, f_curr);
 175                        continue;
 176                }
 177
 178                /* check if current range overlaps with next range. */
 179                if (n_range->lsr_start < c_range->lsr_end) {
 180                        if (c_range->lsr_index == n_range->lsr_index) {
 181                                n_range->lsr_start = c_range->lsr_start;
 182                                n_range->lsr_end = max(c_range->lsr_end,
 183                                                       n_range->lsr_end);
 184                                fld_cache_entry_delete(cache, f_curr);
 185                        } else {
 186                                if (n_range->lsr_end <= c_range->lsr_end) {
 187                                        *n_range = *c_range;
 188                                        fld_cache_entry_delete(cache, f_curr);
 189                                } else
 190                                        n_range->lsr_start = c_range->lsr_end;
 191                        }
 192
 193                        /* we could have overlap over next
 194                         * range too. better restart. */
 195                        goto restart_fixup;
 196                }
 197
 198                /* kill duplicates */
 199                if (c_range->lsr_start == n_range->lsr_start &&
 200                    c_range->lsr_end == n_range->lsr_end)
 201                        fld_cache_entry_delete(cache, f_curr);
 202        }
 203
 204        EXIT;
 205}
 206
 207/**
 208 * add node to fld cache
 209 */
 210static inline void fld_cache_entry_add(struct fld_cache *cache,
 211                                       struct fld_cache_entry *f_new,
 212                                       struct list_head *pos)
 213{
 214        list_add(&f_new->fce_list, pos);
 215        list_add(&f_new->fce_lru, &cache->fci_lru);
 216
 217        cache->fci_cache_count++;
 218        fld_fix_new_list(cache);
 219}
 220
 221/**
 222 * Check if cache needs to be shrunk. If so - do it.
 223 * Remove one entry in list and so on until cache is shrunk enough.
 224 */
 225static int fld_cache_shrink(struct fld_cache *cache)
 226{
 227        struct fld_cache_entry *flde;
 228        struct list_head *curr;
 229        int num = 0;
 230        ENTRY;
 231
 232        LASSERT(cache != NULL);
 233
 234        if (cache->fci_cache_count < cache->fci_cache_size)
 235                RETURN(0);
 236
 237        curr = cache->fci_lru.prev;
 238
 239        while (cache->fci_cache_count + cache->fci_threshold >
 240               cache->fci_cache_size && curr != &cache->fci_lru) {
 241
 242                flde = list_entry(curr, struct fld_cache_entry, fce_lru);
 243                curr = curr->prev;
 244                fld_cache_entry_delete(cache, flde);
 245                num++;
 246        }
 247
 248        CDEBUG(D_INFO, "%s: FLD cache - Shrunk by "
 249               "%d entries\n", cache->fci_name, num);
 250
 251        RETURN(0);
 252}
 253
 254/**
 255 * kill all fld cache entries.
 256 */
 257void fld_cache_flush(struct fld_cache *cache)
 258{
 259        ENTRY;
 260
 261        write_lock(&cache->fci_lock);
 262        cache->fci_cache_size = 0;
 263        fld_cache_shrink(cache);
 264        write_unlock(&cache->fci_lock);
 265
 266        EXIT;
 267}
 268
 269/**
 270 * punch hole in existing range. divide this range and add new
 271 * entry accordingly.
 272 */
 273
 274void fld_cache_punch_hole(struct fld_cache *cache,
 275                          struct fld_cache_entry *f_curr,
 276                          struct fld_cache_entry *f_new)
 277{
 278        const struct lu_seq_range *range = &f_new->fce_range;
 279        const seqno_t new_start  = range->lsr_start;
 280        const seqno_t new_end  = range->lsr_end;
 281        struct fld_cache_entry *fldt;
 282
 283        ENTRY;
 284        OBD_ALLOC_GFP(fldt, sizeof *fldt, GFP_ATOMIC);
 285        if (!fldt) {
 286                OBD_FREE_PTR(f_new);
 287                EXIT;
 288                /* overlap is not allowed, so dont mess up list. */
 289                return;
 290        }
 291        /*  break f_curr RANGE into three RANGES:
 292         *      f_curr, f_new , fldt
 293         */
 294
 295        /* f_new = *range */
 296
 297        /* fldt */
 298        fldt->fce_range.lsr_start = new_end;
 299        fldt->fce_range.lsr_end = f_curr->fce_range.lsr_end;
 300        fldt->fce_range.lsr_index = f_curr->fce_range.lsr_index;
 301
 302        /* f_curr */
 303        f_curr->fce_range.lsr_end = new_start;
 304
 305        /* add these two entries to list */
 306        fld_cache_entry_add(cache, f_new, &f_curr->fce_list);
 307        fld_cache_entry_add(cache, fldt, &f_new->fce_list);
 308
 309        /* no need to fixup */
 310        EXIT;
 311}
 312
 313/**
 314 * handle range overlap in fld cache.
 315 */
 316static void fld_cache_overlap_handle(struct fld_cache *cache,
 317                                struct fld_cache_entry *f_curr,
 318                                struct fld_cache_entry *f_new)
 319{
 320        const struct lu_seq_range *range = &f_new->fce_range;
 321        const seqno_t new_start  = range->lsr_start;
 322        const seqno_t new_end  = range->lsr_end;
 323        const mdsno_t mdt = range->lsr_index;
 324
 325        /* this is overlap case, these case are checking overlapping with
 326         * prev range only. fixup will handle overlaping with next range. */
 327
 328        if (f_curr->fce_range.lsr_index == mdt) {
 329                f_curr->fce_range.lsr_start = min(f_curr->fce_range.lsr_start,
 330                                                  new_start);
 331
 332                f_curr->fce_range.lsr_end = max(f_curr->fce_range.lsr_end,
 333                                                new_end);
 334
 335                OBD_FREE_PTR(f_new);
 336                fld_fix_new_list(cache);
 337
 338        } else if (new_start <= f_curr->fce_range.lsr_start &&
 339                        f_curr->fce_range.lsr_end <= new_end) {
 340                /* case 1: new range completely overshadowed existing range.
 341                 *       e.g. whole range migrated. update fld cache entry */
 342
 343                f_curr->fce_range = *range;
 344                OBD_FREE_PTR(f_new);
 345                fld_fix_new_list(cache);
 346
 347        } else if (f_curr->fce_range.lsr_start < new_start &&
 348                        new_end < f_curr->fce_range.lsr_end) {
 349                /* case 2: new range fit within existing range. */
 350
 351                fld_cache_punch_hole(cache, f_curr, f_new);
 352
 353        } else  if (new_end <= f_curr->fce_range.lsr_end) {
 354                /* case 3: overlap:
 355                 *       [new_start [c_start  new_end)  c_end)
 356                 */
 357
 358                LASSERT(new_start <= f_curr->fce_range.lsr_start);
 359
 360                f_curr->fce_range.lsr_start = new_end;
 361                fld_cache_entry_add(cache, f_new, f_curr->fce_list.prev);
 362
 363        } else if (f_curr->fce_range.lsr_start <= new_start) {
 364                /* case 4: overlap:
 365                 *       [c_start [new_start c_end) new_end)
 366                 */
 367
 368                LASSERT(f_curr->fce_range.lsr_end <= new_end);
 369
 370                f_curr->fce_range.lsr_end = new_start;
 371                fld_cache_entry_add(cache, f_new, &f_curr->fce_list);
 372        } else
 373                CERROR("NEW range ="DRANGE" curr = "DRANGE"\n",
 374                       PRANGE(range),PRANGE(&f_curr->fce_range));
 375}
 376
 377struct fld_cache_entry
 378*fld_cache_entry_create(const struct lu_seq_range *range)
 379{
 380        struct fld_cache_entry *f_new;
 381
 382        LASSERT(range_is_sane(range));
 383
 384        OBD_ALLOC_PTR(f_new);
 385        if (!f_new)
 386                RETURN(ERR_PTR(-ENOMEM));
 387
 388        f_new->fce_range = *range;
 389        RETURN(f_new);
 390}
 391
 392/**
 393 * Insert FLD entry in FLD cache.
 394 *
 395 * This function handles all cases of merging and breaking up of
 396 * ranges.
 397 */
 398int fld_cache_insert_nolock(struct fld_cache *cache,
 399                            struct fld_cache_entry *f_new)
 400{
 401        struct fld_cache_entry *f_curr;
 402        struct fld_cache_entry *n;
 403        struct list_head *head;
 404        struct list_head *prev = NULL;
 405        const seqno_t new_start  = f_new->fce_range.lsr_start;
 406        const seqno_t new_end  = f_new->fce_range.lsr_end;
 407        __u32 new_flags  = f_new->fce_range.lsr_flags;
 408        ENTRY;
 409
 410        /*
 411         * Duplicate entries are eliminated in insert op.
 412         * So we don't need to search new entry before starting
 413         * insertion loop.
 414         */
 415
 416        if (!cache->fci_no_shrink)
 417                fld_cache_shrink(cache);
 418
 419        head = &cache->fci_entries_head;
 420
 421        list_for_each_entry_safe(f_curr, n, head, fce_list) {
 422                /* add list if next is end of list */
 423                if (new_end < f_curr->fce_range.lsr_start ||
 424                   (new_end == f_curr->fce_range.lsr_start &&
 425                    new_flags != f_curr->fce_range.lsr_flags))
 426                        break;
 427
 428                prev = &f_curr->fce_list;
 429                /* check if this range is to left of new range. */
 430                if (new_start < f_curr->fce_range.lsr_end &&
 431                    new_flags == f_curr->fce_range.lsr_flags) {
 432                        fld_cache_overlap_handle(cache, f_curr, f_new);
 433                        goto out;
 434                }
 435        }
 436
 437        if (prev == NULL)
 438                prev = head;
 439
 440        CDEBUG(D_INFO, "insert range "DRANGE"\n", PRANGE(&f_new->fce_range));
 441        /* Add new entry to cache and lru list. */
 442        fld_cache_entry_add(cache, f_new, prev);
 443out:
 444        RETURN(0);
 445}
 446
 447int fld_cache_insert(struct fld_cache *cache,
 448                     const struct lu_seq_range *range)
 449{
 450        struct fld_cache_entry  *flde;
 451        int rc;
 452
 453        flde = fld_cache_entry_create(range);
 454        if (IS_ERR(flde))
 455                RETURN(PTR_ERR(flde));
 456
 457        write_lock(&cache->fci_lock);
 458        rc = fld_cache_insert_nolock(cache, flde);
 459        write_unlock(&cache->fci_lock);
 460        if (rc)
 461                OBD_FREE_PTR(flde);
 462
 463        RETURN(rc);
 464}
 465
 466void fld_cache_delete_nolock(struct fld_cache *cache,
 467                      const struct lu_seq_range *range)
 468{
 469        struct fld_cache_entry *flde;
 470        struct fld_cache_entry *tmp;
 471        struct list_head *head;
 472
 473        head = &cache->fci_entries_head;
 474        list_for_each_entry_safe(flde, tmp, head, fce_list) {
 475                /* add list if next is end of list */
 476                if (range->lsr_start == flde->fce_range.lsr_start ||
 477                   (range->lsr_end == flde->fce_range.lsr_end &&
 478                    range->lsr_flags == flde->fce_range.lsr_flags)) {
 479                        fld_cache_entry_delete(cache, flde);
 480                        break;
 481                }
 482        }
 483}
 484
 485/**
 486 * Delete FLD entry in FLD cache.
 487 *
 488 */
 489void fld_cache_delete(struct fld_cache *cache,
 490                      const struct lu_seq_range *range)
 491{
 492        write_lock(&cache->fci_lock);
 493        fld_cache_delete_nolock(cache, range);
 494        write_unlock(&cache->fci_lock);
 495}
 496
 497struct fld_cache_entry
 498*fld_cache_entry_lookup_nolock(struct fld_cache *cache,
 499                              struct lu_seq_range *range)
 500{
 501        struct fld_cache_entry *flde;
 502        struct fld_cache_entry *got = NULL;
 503        struct list_head *head;
 504
 505        head = &cache->fci_entries_head;
 506        list_for_each_entry(flde, head, fce_list) {
 507                if (range->lsr_start == flde->fce_range.lsr_start ||
 508                   (range->lsr_end == flde->fce_range.lsr_end &&
 509                    range->lsr_flags == flde->fce_range.lsr_flags)) {
 510                        got = flde;
 511                        break;
 512                }
 513        }
 514
 515        RETURN(got);
 516}
 517
 518/**
 519 * lookup \a seq sequence for range in fld cache.
 520 */
 521struct fld_cache_entry
 522*fld_cache_entry_lookup(struct fld_cache *cache, struct lu_seq_range *range)
 523{
 524        struct fld_cache_entry *got = NULL;
 525        ENTRY;
 526
 527        read_lock(&cache->fci_lock);
 528        got = fld_cache_entry_lookup_nolock(cache, range);
 529        read_unlock(&cache->fci_lock);
 530        RETURN(got);
 531}
 532
 533/**
 534 * lookup \a seq sequence for range in fld cache.
 535 */
 536int fld_cache_lookup(struct fld_cache *cache,
 537                     const seqno_t seq, struct lu_seq_range *range)
 538{
 539        struct fld_cache_entry *flde;
 540        struct fld_cache_entry *prev = NULL;
 541        struct list_head *head;
 542        ENTRY;
 543
 544        read_lock(&cache->fci_lock);
 545        head = &cache->fci_entries_head;
 546
 547        cache->fci_stat.fst_count++;
 548        list_for_each_entry(flde, head, fce_list) {
 549                if (flde->fce_range.lsr_start > seq) {
 550                        if (prev != NULL)
 551                                *range = prev->fce_range;
 552                        break;
 553                }
 554
 555                prev = flde;
 556                if (range_within(&flde->fce_range, seq)) {
 557                        *range = flde->fce_range;
 558
 559                        cache->fci_stat.fst_cache++;
 560                        read_unlock(&cache->fci_lock);
 561                        RETURN(0);
 562                }
 563        }
 564        read_unlock(&cache->fci_lock);
 565        RETURN(-ENOENT);
 566}
 567