linux/fs/afs/volume.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/* AFS volume management
   3 *
   4 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
   5 * Written by David Howells (dhowells@redhat.com)
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/slab.h>
  10#include "internal.h"
  11
  12unsigned __read_mostly afs_volume_gc_delay = 10;
  13unsigned __read_mostly afs_volume_record_life = 60 * 60;
  14
  15static const char *const afs_voltypes[] = { "R/W", "R/O", "BAK" };
  16
  17/*
  18 * Allocate a volume record and load it up from a vldb record.
  19 */
  20static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params,
  21                                           struct afs_vldb_entry *vldb,
  22                                           unsigned long type_mask)
  23{
  24        struct afs_server_list *slist;
  25        struct afs_volume *volume;
  26        int ret = -ENOMEM, nr_servers = 0, i;
  27
  28        for (i = 0; i < vldb->nr_servers; i++)
  29                if (vldb->fs_mask[i] & type_mask)
  30                        nr_servers++;
  31
  32        volume = kzalloc(sizeof(struct afs_volume), GFP_KERNEL);
  33        if (!volume)
  34                goto error_0;
  35
  36        volume->vid             = vldb->vid[params->type];
  37        volume->update_at       = ktime_get_real_seconds() + afs_volume_record_life;
  38        volume->cell            = afs_get_cell(params->cell);
  39        volume->type            = params->type;
  40        volume->type_force      = params->force;
  41        volume->name_len        = vldb->name_len;
  42
  43        atomic_set(&volume->usage, 1);
  44        INIT_LIST_HEAD(&volume->proc_link);
  45        rwlock_init(&volume->servers_lock);
  46        rwlock_init(&volume->cb_v_break_lock);
  47        memcpy(volume->name, vldb->name, vldb->name_len + 1);
  48
  49        slist = afs_alloc_server_list(params->cell, params->key, vldb, type_mask);
  50        if (IS_ERR(slist)) {
  51                ret = PTR_ERR(slist);
  52                goto error_1;
  53        }
  54
  55        refcount_set(&slist->usage, 1);
  56        volume->servers = slist;
  57        return volume;
  58
  59error_1:
  60        afs_put_cell(params->net, volume->cell);
  61        kfree(volume);
  62error_0:
  63        return ERR_PTR(ret);
  64}
  65
  66/*
  67 * Look up a VLDB record for a volume.
  68 */
  69static struct afs_vldb_entry *afs_vl_lookup_vldb(struct afs_cell *cell,
  70                                                 struct key *key,
  71                                                 const char *volname,
  72                                                 size_t volnamesz)
  73{
  74        struct afs_vldb_entry *vldb = ERR_PTR(-EDESTADDRREQ);
  75        struct afs_vl_cursor vc;
  76        int ret;
  77
  78        if (!afs_begin_vlserver_operation(&vc, cell, key))
  79                return ERR_PTR(-ERESTARTSYS);
  80
  81        while (afs_select_vlserver(&vc)) {
  82                vldb = afs_vl_get_entry_by_name_u(&vc, volname, volnamesz);
  83        }
  84
  85        ret = afs_end_vlserver_operation(&vc);
  86        return ret < 0 ? ERR_PTR(ret) : vldb;
  87}
  88
  89/*
  90 * Look up a volume in the VL server and create a candidate volume record for
  91 * it.
  92 *
  93 * The volume name can be one of the following:
  94 *      "%[cell:]volume[.]"             R/W volume
  95 *      "#[cell:]volume[.]"             R/O or R/W volume (rwparent=0),
  96 *                                       or R/W (rwparent=1) volume
  97 *      "%[cell:]volume.readonly"       R/O volume
  98 *      "#[cell:]volume.readonly"       R/O volume
  99 *      "%[cell:]volume.backup"         Backup volume
 100 *      "#[cell:]volume.backup"         Backup volume
 101 *
 102 * The cell name is optional, and defaults to the current cell.
 103 *
 104 * See "The Rules of Mount Point Traversal" in Chapter 5 of the AFS SysAdmin
 105 * Guide
 106 * - Rule 1: Explicit type suffix forces access of that type or nothing
 107 *           (no suffix, then use Rule 2 & 3)
 108 * - Rule 2: If parent volume is R/O, then mount R/O volume by preference, R/W
 109 *           if not available
 110 * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
 111 *           explicitly told otherwise
 112 */
 113struct afs_volume *afs_create_volume(struct afs_fs_context *params)
 114{
 115        struct afs_vldb_entry *vldb;
 116        struct afs_volume *volume;
 117        unsigned long type_mask = 1UL << params->type;
 118
 119        vldb = afs_vl_lookup_vldb(params->cell, params->key,
 120                                  params->volname, params->volnamesz);
 121        if (IS_ERR(vldb))
 122                return ERR_CAST(vldb);
 123
 124        if (test_bit(AFS_VLDB_QUERY_ERROR, &vldb->flags)) {
 125                volume = ERR_PTR(vldb->error);
 126                goto error;
 127        }
 128
 129        /* Make the final decision on the type we want */
 130        volume = ERR_PTR(-ENOMEDIUM);
 131        if (params->force) {
 132                if (!(vldb->flags & type_mask))
 133                        goto error;
 134        } else if (test_bit(AFS_VLDB_HAS_RO, &vldb->flags)) {
 135                params->type = AFSVL_ROVOL;
 136        } else if (test_bit(AFS_VLDB_HAS_RW, &vldb->flags)) {
 137                params->type = AFSVL_RWVOL;
 138        } else {
 139                goto error;
 140        }
 141
 142        type_mask = 1UL << params->type;
 143        volume = afs_alloc_volume(params, vldb, type_mask);
 144
 145error:
 146        kfree(vldb);
 147        return volume;
 148}
 149
 150/*
 151 * Destroy a volume record
 152 */
 153static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume)
 154{
 155        _enter("%p", volume);
 156
 157#ifdef CONFIG_AFS_FSCACHE
 158        ASSERTCMP(volume->cache, ==, NULL);
 159#endif
 160
 161        afs_put_serverlist(net, volume->servers);
 162        afs_put_cell(net, volume->cell);
 163        kfree(volume);
 164
 165        _leave(" [destroyed]");
 166}
 167
 168/*
 169 * Drop a reference on a volume record.
 170 */
 171void afs_put_volume(struct afs_cell *cell, struct afs_volume *volume)
 172{
 173        if (volume) {
 174                _enter("%s", volume->name);
 175
 176                if (atomic_dec_and_test(&volume->usage))
 177                        afs_destroy_volume(cell->net, volume);
 178        }
 179}
 180
 181/*
 182 * Activate a volume.
 183 */
 184void afs_activate_volume(struct afs_volume *volume)
 185{
 186#ifdef CONFIG_AFS_FSCACHE
 187        volume->cache = fscache_acquire_cookie(volume->cell->cache,
 188                                               &afs_volume_cache_index_def,
 189                                               &volume->vid, sizeof(volume->vid),
 190                                               NULL, 0,
 191                                               volume, 0, true);
 192#endif
 193
 194        write_lock(&volume->cell->proc_lock);
 195        list_add_tail(&volume->proc_link, &volume->cell->proc_volumes);
 196        write_unlock(&volume->cell->proc_lock);
 197}
 198
 199/*
 200 * Deactivate a volume.
 201 */
 202void afs_deactivate_volume(struct afs_volume *volume)
 203{
 204        _enter("%s", volume->name);
 205
 206        write_lock(&volume->cell->proc_lock);
 207        list_del_init(&volume->proc_link);
 208        write_unlock(&volume->cell->proc_lock);
 209
 210#ifdef CONFIG_AFS_FSCACHE
 211        fscache_relinquish_cookie(volume->cache, NULL,
 212                                  test_bit(AFS_VOLUME_DELETED, &volume->flags));
 213        volume->cache = NULL;
 214#endif
 215
 216        _leave("");
 217}
 218
 219/*
 220 * Query the VL service to update the volume status.
 221 */
 222static int afs_update_volume_status(struct afs_volume *volume, struct key *key)
 223{
 224        struct afs_server_list *new, *old, *discard;
 225        struct afs_vldb_entry *vldb;
 226        char idbuf[16];
 227        int ret, idsz;
 228
 229        _enter("");
 230
 231        /* We look up an ID by passing it as a decimal string in the
 232         * operation's name parameter.
 233         */
 234        idsz = sprintf(idbuf, "%llu", volume->vid);
 235
 236        vldb = afs_vl_lookup_vldb(volume->cell, key, idbuf, idsz);
 237        if (IS_ERR(vldb)) {
 238                ret = PTR_ERR(vldb);
 239                goto error;
 240        }
 241
 242        /* See if the volume got renamed. */
 243        if (vldb->name_len != volume->name_len ||
 244            memcmp(vldb->name, volume->name, vldb->name_len) != 0) {
 245                /* TODO: Use RCU'd string. */
 246                memcpy(volume->name, vldb->name, AFS_MAXVOLNAME);
 247                volume->name_len = vldb->name_len;
 248        }
 249
 250        /* See if the volume's server list got updated. */
 251        new = afs_alloc_server_list(volume->cell, key,
 252                                    vldb, (1 << volume->type));
 253        if (IS_ERR(new)) {
 254                ret = PTR_ERR(new);
 255                goto error_vldb;
 256        }
 257
 258        write_lock(&volume->servers_lock);
 259
 260        discard = new;
 261        old = volume->servers;
 262        if (afs_annotate_server_list(new, old)) {
 263                new->seq = volume->servers_seq + 1;
 264                volume->servers = new;
 265                smp_wmb();
 266                volume->servers_seq++;
 267                discard = old;
 268        }
 269
 270        volume->update_at = ktime_get_real_seconds() + afs_volume_record_life;
 271        clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
 272        write_unlock(&volume->servers_lock);
 273        ret = 0;
 274
 275        afs_put_serverlist(volume->cell->net, discard);
 276error_vldb:
 277        kfree(vldb);
 278error:
 279        _leave(" = %d", ret);
 280        return ret;
 281}
 282
 283/*
 284 * Make sure the volume record is up to date.
 285 */
 286int afs_check_volume_status(struct afs_volume *volume, struct key *key)
 287{
 288        time64_t now = ktime_get_real_seconds();
 289        int ret, retries = 0;
 290
 291        _enter("");
 292
 293        if (volume->update_at <= now)
 294                set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
 295
 296retry:
 297        if (!test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags) &&
 298            !test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
 299                _leave(" = 0");
 300                return 0;
 301        }
 302
 303        if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) {
 304                ret = afs_update_volume_status(volume, key);
 305                clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags);
 306                clear_bit_unlock(AFS_VOLUME_UPDATING, &volume->flags);
 307                wake_up_bit(&volume->flags, AFS_VOLUME_WAIT);
 308                _leave(" = %d", ret);
 309                return ret;
 310        }
 311
 312        if (!test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
 313                _leave(" = 0 [no wait]");
 314                return 0;
 315        }
 316
 317        ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT, TASK_INTERRUPTIBLE);
 318        if (ret == -ERESTARTSYS) {
 319                _leave(" = %d", ret);
 320                return ret;
 321        }
 322
 323        retries++;
 324        if (retries == 4) {
 325                _leave(" = -ESTALE");
 326                return -ESTALE;
 327        }
 328        goto retry;
 329}
 330