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