linux/fs/afs/server_list.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/* AFS fileserver list management.
   3 *
   4 * Copyright (C) 2017 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
  12void afs_put_serverlist(struct afs_net *net, struct afs_server_list *slist)
  13{
  14        int i;
  15
  16        if (slist && refcount_dec_and_test(&slist->usage)) {
  17                for (i = 0; i < slist->nr_servers; i++)
  18                        afs_unuse_server(net, slist->servers[i].server,
  19                                         afs_server_trace_put_slist);
  20                kfree(slist);
  21        }
  22}
  23
  24/*
  25 * Build a server list from a VLDB record.
  26 */
  27struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,
  28                                              struct key *key,
  29                                              struct afs_vldb_entry *vldb,
  30                                              u8 type_mask)
  31{
  32        struct afs_server_list *slist;
  33        struct afs_server *server;
  34        int ret = -ENOMEM, nr_servers = 0, i, j;
  35
  36        for (i = 0; i < vldb->nr_servers; i++)
  37                if (vldb->fs_mask[i] & type_mask)
  38                        nr_servers++;
  39
  40        slist = kzalloc(struct_size(slist, servers, nr_servers), GFP_KERNEL);
  41        if (!slist)
  42                goto error;
  43
  44        refcount_set(&slist->usage, 1);
  45        rwlock_init(&slist->lock);
  46
  47        for (i = 0; i < AFS_MAXTYPES; i++)
  48                slist->vids[i] = vldb->vid[i];
  49
  50        /* Make sure a records exists for each server in the list. */
  51        for (i = 0; i < vldb->nr_servers; i++) {
  52                if (!(vldb->fs_mask[i] & type_mask))
  53                        continue;
  54
  55                server = afs_lookup_server(cell, key, &vldb->fs_server[i],
  56                                           vldb->addr_version[i]);
  57                if (IS_ERR(server)) {
  58                        ret = PTR_ERR(server);
  59                        if (ret == -ENOENT ||
  60                            ret == -ENOMEDIUM)
  61                                continue;
  62                        goto error_2;
  63                }
  64
  65                /* Insertion-sort by UUID */
  66                for (j = 0; j < slist->nr_servers; j++)
  67                        if (memcmp(&slist->servers[j].server->uuid,
  68                                   &server->uuid,
  69                                   sizeof(server->uuid)) >= 0)
  70                                break;
  71                if (j < slist->nr_servers) {
  72                        if (slist->servers[j].server == server) {
  73                                afs_put_server(cell->net, server,
  74                                               afs_server_trace_put_slist_isort);
  75                                continue;
  76                        }
  77
  78                        memmove(slist->servers + j + 1,
  79                                slist->servers + j,
  80                                (slist->nr_servers - j) * sizeof(struct afs_server_entry));
  81                }
  82
  83                slist->servers[j].server = server;
  84                slist->nr_servers++;
  85        }
  86
  87        if (slist->nr_servers == 0) {
  88                ret = -EDESTADDRREQ;
  89                goto error_2;
  90        }
  91
  92        return slist;
  93
  94error_2:
  95        afs_put_serverlist(cell->net, slist);
  96error:
  97        return ERR_PTR(ret);
  98}
  99
 100/*
 101 * Copy the annotations from an old server list to its potential replacement.
 102 */
 103bool afs_annotate_server_list(struct afs_server_list *new,
 104                              struct afs_server_list *old)
 105{
 106        struct afs_server *cur;
 107        int i, j;
 108
 109        if (old->nr_servers != new->nr_servers)
 110                goto changed;
 111
 112        for (i = 0; i < old->nr_servers; i++)
 113                if (old->servers[i].server != new->servers[i].server)
 114                        goto changed;
 115
 116        return false;
 117
 118changed:
 119        /* Maintain the same preferred server as before if possible. */
 120        cur = old->servers[old->preferred].server;
 121        for (j = 0; j < new->nr_servers; j++) {
 122                if (new->servers[j].server == cur) {
 123                        new->preferred = j;
 124                        break;
 125                }
 126        }
 127
 128        return true;
 129}
 130