linux/fs/afs/callback.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved.
   3 *
   4 * This software may be freely redistributed under the terms of the
   5 * GNU General Public License.
   6 *
   7 * You should have received a copy of the GNU General Public License
   8 * along with this program; if not, write to the Free Software
   9 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  10 *
  11 * Authors: David Woodhouse <dwmw2@infradead.org>
  12 *          David Howells <dhowells@redhat.com>
  13 *
  14 */
  15
  16#include <linux/kernel.h>
  17#include <linux/module.h>
  18#include <linux/init.h>
  19#include <linux/circ_buf.h>
  20#include <linux/sched.h>
  21#include "internal.h"
  22
  23/*
  24 * Allow the fileserver to request callback state (re-)initialisation.
  25 * Unfortunately, UUIDs are not guaranteed unique.
  26 */
  27void afs_init_callback_state(struct afs_server *server)
  28{
  29        rcu_read_lock();
  30        do {
  31                server->cb_s_break++;
  32                server = rcu_dereference(server->uuid_next);
  33        } while (0);
  34        rcu_read_unlock();
  35}
  36
  37/*
  38 * actually break a callback
  39 */
  40void __afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason)
  41{
  42        _enter("");
  43
  44        clear_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
  45        if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
  46                vnode->cb_break++;
  47                afs_clear_permits(vnode);
  48
  49                if (vnode->lock_state == AFS_VNODE_LOCK_WAITING_FOR_CB)
  50                        afs_lock_may_be_available(vnode);
  51
  52                trace_afs_cb_break(&vnode->fid, vnode->cb_break, reason, true);
  53        } else {
  54                trace_afs_cb_break(&vnode->fid, vnode->cb_break, reason, false);
  55        }
  56}
  57
  58void afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason)
  59{
  60        write_seqlock(&vnode->cb_lock);
  61        __afs_break_callback(vnode, reason);
  62        write_sequnlock(&vnode->cb_lock);
  63}
  64
  65/*
  66 * Look up a volume by volume ID under RCU conditions.
  67 */
  68static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell,
  69                                                afs_volid_t vid)
  70{
  71        struct afs_volume *volume = NULL;
  72        struct rb_node *p;
  73        int seq = 0;
  74
  75        do {
  76                /* Unfortunately, rbtree walking doesn't give reliable results
  77                 * under just the RCU read lock, so we have to check for
  78                 * changes.
  79                 */
  80                read_seqbegin_or_lock(&cell->volume_lock, &seq);
  81
  82                p = rcu_dereference_raw(cell->volumes.rb_node);
  83                while (p) {
  84                        volume = rb_entry(p, struct afs_volume, cell_node);
  85
  86                        if (volume->vid < vid)
  87                                p = rcu_dereference_raw(p->rb_left);
  88                        else if (volume->vid > vid)
  89                                p = rcu_dereference_raw(p->rb_right);
  90                        else
  91                                break;
  92                        volume = NULL;
  93                }
  94
  95        } while (need_seqretry(&cell->volume_lock, seq));
  96
  97        done_seqretry(&cell->volume_lock, seq);
  98        return volume;
  99}
 100
 101/*
 102 * allow the fileserver to explicitly break one callback
 103 * - happens when
 104 *   - the backing file is changed
 105 *   - a lock is released
 106 */
 107static void afs_break_one_callback(struct afs_volume *volume,
 108                                   struct afs_fid *fid)
 109{
 110        struct super_block *sb;
 111        struct afs_vnode *vnode;
 112        struct inode *inode;
 113
 114        if (fid->vnode == 0 && fid->unique == 0) {
 115                /* The callback break applies to an entire volume. */
 116                write_lock(&volume->cb_v_break_lock);
 117                volume->cb_v_break++;
 118                trace_afs_cb_break(fid, volume->cb_v_break,
 119                                   afs_cb_break_for_volume_callback, false);
 120                write_unlock(&volume->cb_v_break_lock);
 121                return;
 122        }
 123
 124        /* See if we can find a matching inode - even an I_NEW inode needs to
 125         * be marked as it can have its callback broken before we finish
 126         * setting up the local inode.
 127         */
 128        sb = rcu_dereference(volume->sb);
 129        if (!sb)
 130                return;
 131
 132        inode = find_inode_rcu(sb, fid->vnode, afs_ilookup5_test_by_fid, fid);
 133        if (inode) {
 134                vnode = AFS_FS_I(inode);
 135                afs_break_callback(vnode, afs_cb_break_for_callback);
 136        } else {
 137                trace_afs_cb_miss(fid, afs_cb_break_for_callback);
 138        }
 139}
 140
 141static void afs_break_some_callbacks(struct afs_server *server,
 142                                     struct afs_callback_break *cbb,
 143                                     size_t *_count)
 144{
 145        struct afs_callback_break *residue = cbb;
 146        struct afs_volume *volume;
 147        afs_volid_t vid = cbb->fid.vid;
 148        size_t i;
 149
 150        volume = afs_lookup_volume_rcu(server->cell, vid);
 151
 152        /* TODO: Find all matching volumes if we couldn't match the server and
 153         * break them anyway.
 154         */
 155
 156        for (i = *_count; i > 0; cbb++, i--) {
 157                if (cbb->fid.vid == vid) {
 158                        _debug("- Fid { vl=%08llx n=%llu u=%u }",
 159                               cbb->fid.vid,
 160                               cbb->fid.vnode,
 161                               cbb->fid.unique);
 162                        --*_count;
 163                        if (volume)
 164                                afs_break_one_callback(volume, &cbb->fid);
 165                } else {
 166                        *residue++ = *cbb;
 167                }
 168        }
 169}
 170
 171/*
 172 * allow the fileserver to break callback promises
 173 */
 174void afs_break_callbacks(struct afs_server *server, size_t count,
 175                         struct afs_callback_break *callbacks)
 176{
 177        _enter("%p,%zu,", server, count);
 178
 179        ASSERT(server != NULL);
 180
 181        rcu_read_lock();
 182
 183        while (count > 0)
 184                afs_break_some_callbacks(server, callbacks, &count);
 185
 186        rcu_read_unlock();
 187        return;
 188}
 189