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 * Set up an interest-in-callbacks record for a volume on a server and
  25 * register it with the server.
  26 * - Called with vnode->io_lock held.
  27 */
  28int afs_register_server_cb_interest(struct afs_vnode *vnode,
  29                                    struct afs_server_list *slist,
  30                                    unsigned int index)
  31{
  32        struct afs_server_entry *entry = &slist->servers[index];
  33        struct afs_cb_interest *cbi, *vcbi, *new, *old;
  34        struct afs_server *server = entry->server;
  35
  36again:
  37        if (vnode->cb_interest &&
  38            likely(vnode->cb_interest == entry->cb_interest))
  39                return 0;
  40
  41        read_lock(&slist->lock);
  42        cbi = afs_get_cb_interest(entry->cb_interest);
  43        read_unlock(&slist->lock);
  44
  45        vcbi = vnode->cb_interest;
  46        if (vcbi) {
  47                if (vcbi == cbi) {
  48                        afs_put_cb_interest(afs_v2net(vnode), cbi);
  49                        return 0;
  50                }
  51
  52                /* Use a new interest in the server list for the same server
  53                 * rather than an old one that's still attached to a vnode.
  54                 */
  55                if (cbi && vcbi->server == cbi->server) {
  56                        write_seqlock(&vnode->cb_lock);
  57                        old = vnode->cb_interest;
  58                        vnode->cb_interest = cbi;
  59                        write_sequnlock(&vnode->cb_lock);
  60                        afs_put_cb_interest(afs_v2net(vnode), old);
  61                        return 0;
  62                }
  63
  64                /* Re-use the one attached to the vnode. */
  65                if (!cbi && vcbi->server == server) {
  66                        write_lock(&slist->lock);
  67                        if (entry->cb_interest) {
  68                                write_unlock(&slist->lock);
  69                                afs_put_cb_interest(afs_v2net(vnode), cbi);
  70                                goto again;
  71                        }
  72
  73                        entry->cb_interest = cbi;
  74                        write_unlock(&slist->lock);
  75                        return 0;
  76                }
  77        }
  78
  79        if (!cbi) {
  80                new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL);
  81                if (!new)
  82                        return -ENOMEM;
  83
  84                refcount_set(&new->usage, 1);
  85                new->sb = vnode->vfs_inode.i_sb;
  86                new->vid = vnode->volume->vid;
  87                new->server = afs_get_server(server);
  88                INIT_LIST_HEAD(&new->cb_link);
  89
  90                write_lock(&server->cb_break_lock);
  91                list_add_tail(&new->cb_link, &server->cb_interests);
  92                write_unlock(&server->cb_break_lock);
  93
  94                write_lock(&slist->lock);
  95                if (!entry->cb_interest) {
  96                        entry->cb_interest = afs_get_cb_interest(new);
  97                        cbi = new;
  98                        new = NULL;
  99                } else {
 100                        cbi = afs_get_cb_interest(entry->cb_interest);
 101                }
 102                write_unlock(&slist->lock);
 103                afs_put_cb_interest(afs_v2net(vnode), new);
 104        }
 105
 106        ASSERT(cbi);
 107
 108        /* Change the server the vnode is using.  This entails scrubbing any
 109         * interest the vnode had in the previous server it was using.
 110         */
 111        write_seqlock(&vnode->cb_lock);
 112
 113        old = vnode->cb_interest;
 114        vnode->cb_interest = cbi;
 115        vnode->cb_s_break = cbi->server->cb_s_break;
 116        vnode->cb_v_break = vnode->volume->cb_v_break;
 117        clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
 118
 119        write_sequnlock(&vnode->cb_lock);
 120        afs_put_cb_interest(afs_v2net(vnode), old);
 121        return 0;
 122}
 123
 124/*
 125 * Remove an interest on a server.
 126 */
 127void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi)
 128{
 129        if (cbi && refcount_dec_and_test(&cbi->usage)) {
 130                if (!list_empty(&cbi->cb_link)) {
 131                        write_lock(&cbi->server->cb_break_lock);
 132                        list_del_init(&cbi->cb_link);
 133                        write_unlock(&cbi->server->cb_break_lock);
 134                        afs_put_server(net, cbi->server);
 135                }
 136                kfree(cbi);
 137        }
 138}
 139
 140/*
 141 * allow the fileserver to request callback state (re-)initialisation
 142 */
 143void afs_init_callback_state(struct afs_server *server)
 144{
 145        if (!test_and_clear_bit(AFS_SERVER_FL_NEW, &server->flags))
 146                server->cb_s_break++;
 147}
 148
 149/*
 150 * actually break a callback
 151 */
 152void afs_break_callback(struct afs_vnode *vnode)
 153{
 154        _enter("");
 155
 156        write_seqlock(&vnode->cb_lock);
 157
 158        clear_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
 159        if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
 160                vnode->cb_break++;
 161                afs_clear_permits(vnode);
 162
 163                spin_lock(&vnode->lock);
 164
 165                _debug("break callback");
 166
 167                if (list_empty(&vnode->granted_locks) &&
 168                    !list_empty(&vnode->pending_locks))
 169                        afs_lock_may_be_available(vnode);
 170                spin_unlock(&vnode->lock);
 171        }
 172
 173        write_sequnlock(&vnode->cb_lock);
 174}
 175
 176/*
 177 * allow the fileserver to explicitly break one callback
 178 * - happens when
 179 *   - the backing file is changed
 180 *   - a lock is released
 181 */
 182static void afs_break_one_callback(struct afs_server *server,
 183                                   struct afs_fid *fid)
 184{
 185        struct afs_cb_interest *cbi;
 186        struct afs_iget_data data;
 187        struct afs_vnode *vnode;
 188        struct inode *inode;
 189
 190        read_lock(&server->cb_break_lock);
 191
 192        /* Step through all interested superblocks.  There may be more than one
 193         * because of cell aliasing.
 194         */
 195        list_for_each_entry(cbi, &server->cb_interests, cb_link) {
 196                if (cbi->vid != fid->vid)
 197                        continue;
 198
 199                if (fid->vnode == 0 && fid->unique == 0) {
 200                        /* The callback break applies to an entire volume. */
 201                        struct afs_super_info *as = AFS_FS_S(cbi->sb);
 202                        struct afs_volume *volume = as->volume;
 203
 204                        write_lock(&volume->cb_break_lock);
 205                        volume->cb_v_break++;
 206                        write_unlock(&volume->cb_break_lock);
 207                } else {
 208                        data.volume = NULL;
 209                        data.fid = *fid;
 210                        inode = ilookup5_nowait(cbi->sb, fid->vnode,
 211                                                afs_iget5_test, &data);
 212                        if (inode) {
 213                                vnode = AFS_FS_I(inode);
 214                                afs_break_callback(vnode);
 215                                iput(inode);
 216                        }
 217                }
 218        }
 219
 220        read_unlock(&server->cb_break_lock);
 221}
 222
 223/*
 224 * allow the fileserver to break callback promises
 225 */
 226void afs_break_callbacks(struct afs_server *server, size_t count,
 227                         struct afs_callback_break *callbacks)
 228{
 229        _enter("%p,%zu,", server, count);
 230
 231        ASSERT(server != NULL);
 232        ASSERTCMP(count, <=, AFSCBMAX);
 233
 234        /* TODO: Sort the callback break list by volume ID */
 235
 236        for (; count > 0; callbacks++, count--) {
 237                _debug("- Fid { vl=%08x n=%u u=%u }  CB { v=%u x=%u t=%u }",
 238                       callbacks->fid.vid,
 239                       callbacks->fid.vnode,
 240                       callbacks->fid.unique,
 241                       callbacks->cb.version,
 242                       callbacks->cb.expiry,
 243                       callbacks->cb.type
 244                       );
 245                afs_break_one_callback(server, &callbacks->fid);
 246        }
 247
 248        _leave("");
 249        return;
 250}
 251
 252/*
 253 * Clear the callback interests in a server list.
 254 */
 255void afs_clear_callback_interests(struct afs_net *net, struct afs_server_list *slist)
 256{
 257        int i;
 258
 259        for (i = 0; i < slist->nr_servers; i++) {
 260                afs_put_cb_interest(net, slist->servers[i].cb_interest);
 261                slist->servers[i].cb_interest = NULL;
 262        }
 263}
 264