-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEqG5UsNXhtOCrfGQP+7dXa6fLC2sFAl7ZC5kACgkQ+7dXa6fL
 C2uv9A/+NKlTSXyv2ZuvtmXADelndcXJ+nC+3bwI7Jh43aa8uCCsAVYD0VE+dxor
 Ingj/LUJ2sjjp6RXCeeqqETXCoCVt0zK2g216+An7k84KJ+ms+MDa8dNN7l6280S
 1jw4hnT0+g9Ln6elgqBroV980MJC2NGL0Eaete8zFO8UqYZy5w1ge0HfGck2l45U
 2lr6egCWYSUPmtFKXJnLV8luwRvq7DzvTk9WrJu3kwOjaY1AQP1+1VpdhChJLrRc
 /4Ddy1On5IXiFrPi5OtHA422bfirUpIv2HbmI047W9uiZ05MiXwSvNS1qJLTa1AA
 T/SK88d3FCeSYw3olAne2kEl9uewvGByr98fDKFOcDHZj18abd9/VtUp33RXxYBy
 lN2wqlWP++LlZ4sMCbbvLXX8OB1tekQzWQC0vJ5rhRSgveOlhL9TLG2Y05xokFs+
 AwK8zTlDIZ6Pa/JIHfp2E0ZhXEazWTSmP+d7NkgzF0iiORukvsmxjOVUZC4+UCqK
 rYN6goJ5g8qpejRv5NhfP6/olb1NK33f/F2QSSFfxv9zda4HNlayvcoSnFrdUEnt
 IfBhSKPkeDVWs1yse7glDuw19tHp94B9UYwJ46qfHngQPArgy+gp23d0cSy41Pr5
 FRQ23eNvBWIP4srt1gSCBexSGA1h/ACji41CPTJbF2jg5uWFAUE=
 =YVwD
 -----END PGP SIGNATURE-----

Merge tag 'afs-next-20200604' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

Pull AFS updates from David Howells:
 "There's some core VFS changes which affect a couple of filesystems:

   - Make the inode hash table RCU safe and providing some RCU-safe
     accessor functions. The search can then be done without taking the
     inode_hash_lock. Care must be taken because the object may be being
     deleted and no wait is made.

   - Allow iunique() to avoid taking the inode_hash_lock.

   - Allow AFS's callback processing to avoid taking the inode_hash_lock
     when using the inode table to find an inode to notify.

   - Improve Ext4's time updating. Konstantin Khlebnikov said "For now,
     I've plugged this issue with try-lock in ext4 lazy time update.
     This solution is much better."

  Then there's a set of changes to make a number of improvements to the
  AFS driver:

   - Improve callback (ie. third party change notification) processing
     by:

      (a) Relying more on the fact we're doing this under RCU and by
          using fewer locks. This makes use of the RCU-based inode
          searching outlined above.

      (b) Moving to keeping volumes in a tree indexed by volume ID
          rather than a flat list.

      (c) Making the server and volume records logically part of the
          cell. This means that a server record now points directly at
          the cell and the tree of volumes is there. This removes an N:M
          mapping table, simplifying things.

   - Improve keeping NAT or firewall channels open for the server
     callbacks to reach the client by actively polling the fileserver on
     a timed basis, instead of only doing it when we have an operation
     to process.

   - Improving detection of delayed or lost callbacks by including the
     parent directory in the list of file IDs to be queried when doing a
     bulk status fetch from lookup. We can then check to see if our copy
     of the directory has changed under us without us getting notified.

   - Determine aliasing of cells (such as a cell that is pointed to be a
     DNS alias). This allows us to avoid having ambiguity due to
     apparently different cells using the same volume and file servers.

   - Improve the fileserver rotation to do more probing when it detects
     that all of the addresses to a server are listed as non-responsive.
     It's possible that an address that previously stopped responding
     has become responsive again.

  Beyond that, lay some foundations for making some calls asynchronous:

   - Turn the fileserver cursor struct into a general operation struct
     and hang the parameters off of that rather than keeping them in
     local variables and hang results off of that rather than the call
     struct.

   - Implement some general operation handling code and simplify the
     callers of operations that affect a volume or a volume component
     (such as a file). Most of the operation is now done by core code.

   - Operations are supplied with a table of operations to issue
     different variants of RPCs and to manage the completion, where all
     the required data is held in the operation object, thereby allowing
     these to be called from a workqueue.

   - Put the standard "if (begin), while(select), call op, end" sequence
     into a canned function that just emulates the current behaviour for
     now.

  There are also some fixes interspersed:

   - Don't let the EACCES from ICMP6 mapping reach the user as such,
     since it's confusing as to whether it's a filesystem error. Convert
     it to EHOSTUNREACH.

   - Don't use the epoch value acquired through probing a server. If we
     have two servers with the same UUID but in different cells, it's
     hard to draw conclusions from them having different epoch values.

   - Don't interpret the argument to the CB.ProbeUuid RPC as a
     fileserver UUID and look up a fileserver from it.

   - Deal with servers in different cells having the same UUIDs. In the
     event that a CB.InitCallBackState3 RPC is received, we have to
     break the callback promises for every server record matching that
     UUID.

   - Don't let afs_statfs return values that go below 0.

   - Don't use running fileserver probe state to make server selection
     and address selection decisions on. Only make decisions on final
     state as the running state is cleared at the start of probing"

Acked-by: Al Viro <viro@zeniv.linux.org.uk> (fs/inode.c part)

* tag 'afs-next-20200604' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: (27 commits)
  afs: Adjust the fileserver rotation algorithm to reprobe/retry more quickly
  afs: Show more a bit more server state in /proc/net/afs/servers
  afs: Don't use probe running state to make decisions outside probe code
  afs: Fix afs_statfs() to not let the values go below zero
  afs: Fix the by-UUID server tree to allow servers with the same UUID
  afs: Reorganise volume and server trees to be rooted on the cell
  afs: Add a tracepoint to track the lifetime of the afs_volume struct
  afs: Detect cell aliases 3 - YFS Cells with a canonical cell name op
  afs: Detect cell aliases 2 - Cells with no root volumes
  afs: Detect cell aliases 1 - Cells with root volumes
  afs: Implement client support for the YFSVL.GetCellName RPC op
  afs: Retain more of the VLDB record for alias detection
  afs: Fix handling of CB.ProbeUuid cache manager op
  afs: Don't get epoch from a server because it may be ambiguous
  afs: Build an abstraction around an "operation" concept
  afs: Rename struct afs_fs_cursor to afs_operation
  afs: Remove the error argument from afs_protocol_error()
  afs: Set error flag rather than return error from file status decode
  afs: Make callback processing more efficient.
  afs: Show more information in /proc/net/afs/servers
  ...
This commit is contained in:
Linus Torvalds 2020-06-05 16:26:36 -07:00
Родитель 0b166a57e6 8409f67b64
Коммит 9daa0a27a0
38 изменённых файлов: 4453 добавлений и 3939 удалений

Просмотреть файл

@ -18,6 +18,7 @@ kafs-y := \
file.o \
flock.o \
fsclient.o \
fs_operation.o \
fs_probe.o \
inode.o \
main.o \
@ -30,6 +31,7 @@ kafs-y := \
server_list.o \
super.o \
vlclient.o \
vl_alias.o \
vl_list.o \
vl_probe.o \
vl_rotate.o \

Просмотреть файл

@ -10,7 +10,7 @@
#include <linux/in.h>
#define AFS_MAXCELLNAME 64 /* Maximum length of a cell name */
#define AFS_MAXCELLNAME 256 /* Maximum length of a cell name */
#define AFS_MAXVOLNAME 64 /* Maximum length of a volume name */
#define AFS_MAXNSERVERS 8 /* Maximum servers in a basic volume record */
#define AFS_NMAXNSERVERS 13 /* Maximum servers in a N/U-class volume record */
@ -146,7 +146,6 @@ struct afs_file_status {
struct afs_status_cb {
struct afs_file_status status;
struct afs_callback callback;
unsigned int cb_break; /* Pre-op callback break counter */
bool have_status; /* True if status record was retrieved */
bool have_cb; /* True if cb record was retrieved */
bool have_error; /* True if status.abort_code indicates an error */

Просмотреть файл

@ -22,6 +22,7 @@ enum AFSVL_Operations {
VLGETENTRYBYNAMEU = 527, /* AFS Get VLDB entry by name (UUID-variant) */
VLGETADDRSU = 533, /* AFS Get addrs for fileserver */
YVLGETENDPOINTS = 64002, /* YFS Get endpoints for file/volume server */
YVLGETCELLNAME = 64014, /* YFS Get actual cell name */
VLGETCAPABILITIES = 65537, /* AFS Get server capabilities */
};

Просмотреть файл

@ -21,192 +21,17 @@
#include "internal.h"
/*
* Create volume and callback interests on a server.
*/
static struct afs_cb_interest *afs_create_interest(struct afs_server *server,
struct afs_vnode *vnode)
{
struct afs_vol_interest *new_vi, *vi;
struct afs_cb_interest *new;
struct hlist_node **pp;
new_vi = kzalloc(sizeof(struct afs_vol_interest), GFP_KERNEL);
if (!new_vi)
return NULL;
new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL);
if (!new) {
kfree(new_vi);
return NULL;
}
new_vi->usage = 1;
new_vi->vid = vnode->volume->vid;
INIT_HLIST_NODE(&new_vi->srv_link);
INIT_HLIST_HEAD(&new_vi->cb_interests);
refcount_set(&new->usage, 1);
new->sb = vnode->vfs_inode.i_sb;
new->vid = vnode->volume->vid;
new->server = afs_get_server(server, afs_server_trace_get_new_cbi);
INIT_HLIST_NODE(&new->cb_vlink);
write_lock(&server->cb_break_lock);
for (pp = &server->cb_volumes.first; *pp; pp = &(*pp)->next) {
vi = hlist_entry(*pp, struct afs_vol_interest, srv_link);
if (vi->vid < new_vi->vid)
continue;
if (vi->vid > new_vi->vid)
break;
vi->usage++;
goto found_vi;
}
new_vi->srv_link.pprev = pp;
new_vi->srv_link.next = *pp;
if (*pp)
(*pp)->pprev = &new_vi->srv_link.next;
*pp = &new_vi->srv_link;
vi = new_vi;
new_vi = NULL;
found_vi:
new->vol_interest = vi;
hlist_add_head(&new->cb_vlink, &vi->cb_interests);
write_unlock(&server->cb_break_lock);
kfree(new_vi);
return new;
}
/*
* Set up an interest-in-callbacks record for a volume on a server and
* register it with the server.
* - Called with vnode->io_lock held.
*/
int afs_register_server_cb_interest(struct afs_vnode *vnode,
struct afs_server_list *slist,
unsigned int index)
{
struct afs_server_entry *entry = &slist->servers[index];
struct afs_cb_interest *cbi, *vcbi, *new, *old;
struct afs_server *server = entry->server;
again:
vcbi = rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->io_lock));
if (vcbi && likely(vcbi == entry->cb_interest))
return 0;
read_lock(&slist->lock);
cbi = afs_get_cb_interest(entry->cb_interest);
read_unlock(&slist->lock);
if (vcbi) {
if (vcbi == cbi) {
afs_put_cb_interest(afs_v2net(vnode), cbi);
return 0;
}
/* Use a new interest in the server list for the same server
* rather than an old one that's still attached to a vnode.
*/
if (cbi && vcbi->server == cbi->server) {
write_seqlock(&vnode->cb_lock);
old = rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->cb_lock.lock));
rcu_assign_pointer(vnode->cb_interest, cbi);
write_sequnlock(&vnode->cb_lock);
afs_put_cb_interest(afs_v2net(vnode), old);
return 0;
}
/* Re-use the one attached to the vnode. */
if (!cbi && vcbi->server == server) {
write_lock(&slist->lock);
if (entry->cb_interest) {
write_unlock(&slist->lock);
afs_put_cb_interest(afs_v2net(vnode), cbi);
goto again;
}
entry->cb_interest = cbi;
write_unlock(&slist->lock);
return 0;
}
}
if (!cbi) {
new = afs_create_interest(server, vnode);
if (!new)
return -ENOMEM;
write_lock(&slist->lock);
if (!entry->cb_interest) {
entry->cb_interest = afs_get_cb_interest(new);
cbi = new;
new = NULL;
} else {
cbi = afs_get_cb_interest(entry->cb_interest);
}
write_unlock(&slist->lock);
afs_put_cb_interest(afs_v2net(vnode), new);
}
ASSERT(cbi);
/* Change the server the vnode is using. This entails scrubbing any
* interest the vnode had in the previous server it was using.
*/
write_seqlock(&vnode->cb_lock);
old = rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->cb_lock.lock));
rcu_assign_pointer(vnode->cb_interest, cbi);
vnode->cb_s_break = cbi->server->cb_s_break;
vnode->cb_v_break = vnode->volume->cb_v_break;
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
write_sequnlock(&vnode->cb_lock);
afs_put_cb_interest(afs_v2net(vnode), old);
return 0;
}
/*
* Remove an interest on a server.
*/
void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi)
{
struct afs_vol_interest *vi;
if (cbi && refcount_dec_and_test(&cbi->usage)) {
if (!hlist_unhashed(&cbi->cb_vlink)) {
write_lock(&cbi->server->cb_break_lock);
hlist_del_init(&cbi->cb_vlink);
vi = cbi->vol_interest;
cbi->vol_interest = NULL;
if (--vi->usage == 0)
hlist_del(&vi->srv_link);
else
vi = NULL;
write_unlock(&cbi->server->cb_break_lock);
if (vi)
kfree_rcu(vi, rcu);
afs_put_server(net, cbi->server, afs_server_trace_put_cbi);
}
kfree_rcu(cbi, rcu);
}
}
/*
* allow the fileserver to request callback state (re-)initialisation
* Allow the fileserver to request callback state (re-)initialisation.
* Unfortunately, UUIDs are not guaranteed unique.
*/
void afs_init_callback_state(struct afs_server *server)
{
server->cb_s_break++;
rcu_read_lock();
do {
server->cb_s_break++;
server = rcu_dereference(server->uuid_next);
} while (0);
rcu_read_unlock();
}
/*
@ -237,70 +62,110 @@ void afs_break_callback(struct afs_vnode *vnode, enum afs_cb_break_reason reason
write_sequnlock(&vnode->cb_lock);
}
/*
* Look up a volume by volume ID under RCU conditions.
*/
static struct afs_volume *afs_lookup_volume_rcu(struct afs_cell *cell,
afs_volid_t vid)
{
struct afs_volume *volume = NULL;
struct rb_node *p;
int seq = 0;
do {
/* Unfortunately, rbtree walking doesn't give reliable results
* under just the RCU read lock, so we have to check for
* changes.
*/
read_seqbegin_or_lock(&cell->volume_lock, &seq);
p = rcu_dereference_raw(cell->volumes.rb_node);
while (p) {
volume = rb_entry(p, struct afs_volume, cell_node);
if (volume->vid < vid)
p = rcu_dereference_raw(p->rb_left);
else if (volume->vid > vid)
p = rcu_dereference_raw(p->rb_right);
else
break;
volume = NULL;
}
} while (need_seqretry(&cell->volume_lock, seq));
done_seqretry(&cell->volume_lock, seq);
return volume;
}
/*
* allow the fileserver to explicitly break one callback
* - happens when
* - the backing file is changed
* - a lock is released
*/
static void afs_break_one_callback(struct afs_server *server,
static void afs_break_one_callback(struct afs_volume *volume,
struct afs_fid *fid)
{
struct afs_vol_interest *vi;
struct afs_cb_interest *cbi;
struct afs_iget_data data;
struct super_block *sb;
struct afs_vnode *vnode;
struct inode *inode;
read_lock(&server->cb_break_lock);
hlist_for_each_entry(vi, &server->cb_volumes, srv_link) {
if (vi->vid < fid->vid)
continue;
if (vi->vid > fid->vid) {
vi = NULL;
break;
}
//atomic_inc(&vi->usage);
break;
if (fid->vnode == 0 && fid->unique == 0) {
/* The callback break applies to an entire volume. */
write_lock(&volume->cb_v_break_lock);
volume->cb_v_break++;
trace_afs_cb_break(fid, volume->cb_v_break,
afs_cb_break_for_volume_callback, false);
write_unlock(&volume->cb_v_break_lock);
return;
}
/* See if we can find a matching inode - even an I_NEW inode needs to
* be marked as it can have its callback broken before we finish
* setting up the local inode.
*/
sb = rcu_dereference(volume->sb);
if (!sb)
return;
inode = find_inode_rcu(sb, fid->vnode, afs_ilookup5_test_by_fid, fid);
if (inode) {
vnode = AFS_FS_I(inode);
afs_break_callback(vnode, afs_cb_break_for_callback);
} else {
trace_afs_cb_miss(fid, afs_cb_break_for_callback);
}
}
static void afs_break_some_callbacks(struct afs_server *server,
struct afs_callback_break *cbb,
size_t *_count)
{
struct afs_callback_break *residue = cbb;
struct afs_volume *volume;
afs_volid_t vid = cbb->fid.vid;
size_t i;
volume = afs_lookup_volume_rcu(server->cell, vid);
/* TODO: Find all matching volumes if we couldn't match the server and
* break them anyway.
*/
if (!vi)
goto out;
/* Step through all interested superblocks. There may be more than one
* because of cell aliasing.
*/
hlist_for_each_entry(cbi, &vi->cb_interests, cb_vlink) {
if (fid->vnode == 0 && fid->unique == 0) {
/* The callback break applies to an entire volume. */
struct afs_super_info *as = AFS_FS_S(cbi->sb);
struct afs_volume *volume = as->volume;
write_lock(&volume->cb_v_break_lock);
volume->cb_v_break++;
trace_afs_cb_break(fid, volume->cb_v_break,
afs_cb_break_for_volume_callback, false);
write_unlock(&volume->cb_v_break_lock);
for (i = *_count; i > 0; cbb++, i--) {
if (cbb->fid.vid == vid) {
_debug("- Fid { vl=%08llx n=%llu u=%u }",
cbb->fid.vid,
cbb->fid.vnode,
cbb->fid.unique);
--*_count;
if (volume)
afs_break_one_callback(volume, &cbb->fid);
} else {
data.volume = NULL;
data.fid = *fid;
inode = ilookup5_nowait(cbi->sb, fid->vnode,
afs_iget5_test, &data);
if (inode) {
vnode = AFS_FS_I(inode);
afs_break_callback(vnode, afs_cb_break_for_callback);
iput(inode);
} else {
trace_afs_cb_miss(fid, afs_cb_break_for_callback);
}
*residue++ = *cbb;
}
}
out:
read_unlock(&server->cb_break_lock);
}
/*
@ -313,29 +178,11 @@ void afs_break_callbacks(struct afs_server *server, size_t count,
ASSERT(server != NULL);
/* TODO: Sort the callback break list by volume ID */
rcu_read_lock();
for (; count > 0; callbacks++, count--) {
_debug("- Fid { vl=%08llx n=%llu u=%u }",
callbacks->fid.vid,
callbacks->fid.vnode,
callbacks->fid.unique);
afs_break_one_callback(server, &callbacks->fid);
}
while (count > 0)
afs_break_some_callbacks(server, callbacks, &count);
_leave("");
rcu_read_unlock();
return;
}
/*
* Clear the callback interests in a server list.
*/
void afs_clear_callback_interests(struct afs_net *net, struct afs_server_list *slist)
{
int i;
for (i = 0; i < slist->nr_servers; i++) {
afs_put_cb_interest(net, slist->servers[i].cb_interest);
slist->servers[i].cb_interest = NULL;
}
}

Просмотреть файл

@ -161,9 +161,13 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
atomic_set(&cell->usage, 2);
INIT_WORK(&cell->manager, afs_manage_cell);
INIT_LIST_HEAD(&cell->proc_volumes);
rwlock_init(&cell->proc_lock);
cell->volumes = RB_ROOT;
INIT_HLIST_HEAD(&cell->proc_volumes);
seqlock_init(&cell->volume_lock);
cell->fs_servers = RB_ROOT;
seqlock_init(&cell->fs_lock);
rwlock_init(&cell->vl_servers_lock);
cell->flags = (1 << AFS_CELL_FL_CHECK_ALIAS);
/* Provide a VL server list, filling it in if we were given a list of
* addresses to use.
@ -481,7 +485,9 @@ static void afs_cell_destroy(struct rcu_head *rcu)
ASSERTCMP(atomic_read(&cell->usage), ==, 0);
afs_put_volume(cell->net, cell->root_volume, afs_volume_trace_put_cell_root);
afs_put_vlserverlist(cell->net, rcu_access_pointer(cell->vl_servers));
afs_put_cell(cell->net, cell->alias_of);
key_put(cell->anonymous_key);
kfree(cell);

Просмотреть файл

@ -118,8 +118,6 @@ bool afs_cm_incoming_call(struct afs_call *call)
{
_enter("{%u, CB.OP %u}", call->service_id, call->operation_ID);
call->epoch = rxrpc_kernel_get_epoch(call->net->socket, call->rxcall);
switch (call->operation_ID) {
case CBCallBack:
call->type = &afs_SRXCBCallBack;
@ -149,49 +147,6 @@ bool afs_cm_incoming_call(struct afs_call *call)
}
}
/*
* Record a probe to the cache manager from a server.
*/
static int afs_record_cm_probe(struct afs_call *call, struct afs_server *server)
{
_enter("");
if (test_bit(AFS_SERVER_FL_HAVE_EPOCH, &server->flags) &&
!test_bit(AFS_SERVER_FL_PROBING, &server->flags)) {
if (server->cm_epoch == call->epoch)
return 0;
if (!server->probe.said_rebooted) {
pr_notice("kAFS: FS rebooted %pU\n", &server->uuid);
server->probe.said_rebooted = true;
}
}
spin_lock(&server->probe_lock);
if (!test_and_set_bit(AFS_SERVER_FL_HAVE_EPOCH, &server->flags)) {
server->cm_epoch = call->epoch;
server->probe.cm_epoch = call->epoch;
goto out;
}
if (server->probe.cm_probed &&
call->epoch != server->probe.cm_epoch &&
!server->probe.said_inconsistent) {
pr_notice("kAFS: FS endpoints inconsistent %pU\n",
&server->uuid);
server->probe.said_inconsistent = true;
}
if (!server->probe.cm_probed || call->epoch == server->cm_epoch)
server->probe.cm_epoch = server->cm_epoch;
out:
server->probe.cm_probed = true;
spin_unlock(&server->probe_lock);
return 0;
}
/*
* Find the server record by peer address and record a probe to the cache
* manager from a server.
@ -210,7 +165,7 @@ static int afs_find_cm_server_by_peer(struct afs_call *call)
}
call->server = server;
return afs_record_cm_probe(call, server);
return 0;
}
/*
@ -231,7 +186,7 @@ static int afs_find_cm_server_by_uuid(struct afs_call *call,
}
call->server = server;
return afs_record_cm_probe(call, server);
return 0;
}
/*
@ -268,7 +223,9 @@ static void SRXAFSCB_CallBack(struct work_struct *work)
* to maintain cache coherency.
*/
if (call->server) {
trace_afs_server(call->server, atomic_read(&call->server->usage),
trace_afs_server(call->server,
atomic_read(&call->server->ref),
atomic_read(&call->server->active),
afs_server_trace_callback);
afs_break_callbacks(call->server, call->count, call->request);
}
@ -305,8 +262,7 @@ static int afs_deliver_cb_callback(struct afs_call *call)
call->count = ntohl(call->tmp);
_debug("FID count: %u", call->count);
if (call->count > AFSCBMAX)
return afs_protocol_error(call, -EBADMSG,
afs_eproto_cb_fid_count);
return afs_protocol_error(call, afs_eproto_cb_fid_count);
call->buffer = kmalloc(array3_size(call->count, 3, 4),
GFP_KERNEL);
@ -351,8 +307,7 @@ static int afs_deliver_cb_callback(struct afs_call *call)
call->count2 = ntohl(call->tmp);
_debug("CB count: %u", call->count2);
if (call->count2 != call->count && call->count2 != 0)
return afs_protocol_error(call, -EBADMSG,
afs_eproto_cb_count);
return afs_protocol_error(call, afs_eproto_cb_count);
call->iter = &call->def_iter;
iov_iter_discard(&call->def_iter, READ, call->count2 * 3 * 4);
call->unmarshall++;
@ -509,7 +464,8 @@ static int afs_deliver_cb_probe(struct afs_call *call)
}
/*
* allow the fileserver to quickly find out if the fileserver has been rebooted
* Allow the fileserver to quickly find out if the cache manager has been
* rebooted.
*/
static void SRXAFSCB_ProbeUuid(struct work_struct *work)
{
@ -581,7 +537,7 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call)
if (!afs_check_call_state(call, AFS_CALL_SV_REPLYING))
return afs_io_error(call, afs_io_error_cm_reply);
return afs_find_cm_server_by_uuid(call, call->request);
return afs_find_cm_server_by_peer(call);
}
/*
@ -672,8 +628,7 @@ static int afs_deliver_yfs_cb_callback(struct afs_call *call)
call->count = ntohl(call->tmp);
_debug("FID count: %u", call->count);
if (call->count > YFSCBMAX)
return afs_protocol_error(call, -EBADMSG,
afs_eproto_cb_fid_count);
return afs_protocol_error(call, afs_eproto_cb_fid_count);
size = array_size(call->count, sizeof(struct yfs_xdr_YFSFid));
call->buffer = kmalloc(size, GFP_KERNEL);

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -12,6 +12,47 @@
#include <linux/fsnotify.h>
#include "internal.h"
static void afs_silly_rename_success(struct afs_operation *op)
{
_enter("op=%08x", op->debug_id);
afs_vnode_commit_status(op, &op->file[0]);
}
static void afs_silly_rename_edit_dir(struct afs_operation *op)
{
struct afs_vnode_param *dvp = &op->file[0];
struct afs_vnode *dvnode = dvp->vnode;
struct afs_vnode *vnode = AFS_FS_I(d_inode(op->dentry));
struct dentry *old = op->dentry;
struct dentry *new = op->dentry_2;
spin_lock(&old->d_lock);
old->d_flags |= DCACHE_NFSFS_RENAMED;
spin_unlock(&old->d_lock);
if (dvnode->silly_key != op->key) {
key_put(dvnode->silly_key);
dvnode->silly_key = key_get(op->key);
}
down_write(&dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
dvnode->status.data_version == dvp->dv_before + dvp->dv_delta) {
afs_edit_dir_remove(dvnode, &old->d_name,
afs_edit_dir_for_silly_0);
afs_edit_dir_add(dvnode, &new->d_name,
&vnode->fid, afs_edit_dir_for_silly_1);
}
up_write(&dvnode->validate_lock);
}
static const struct afs_operation_ops afs_silly_rename_operation = {
.issue_afs_rpc = afs_fs_rename,
.issue_yfs_rpc = yfs_fs_rename,
.success = afs_silly_rename_success,
.edit_dir = afs_silly_rename_edit_dir,
};
/*
* Actually perform the silly rename step.
*/
@ -19,56 +60,22 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
struct dentry *old, struct dentry *new,
struct key *key)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
afs_dataversion_t dir_data_version;
int ret = -ERESTARTSYS;
struct afs_operation *op;
_enter("%pd,%pd", old, new);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
op = afs_alloc_operation(key, dvnode->volume);
if (IS_ERR(op))
return PTR_ERR(op);
afs_op_set_vnode(op, 0, dvnode);
op->dentry = old;
op->dentry_2 = new;
op->ops = &afs_silly_rename_operation;
trace_afs_silly_rename(vnode, false);
if (afs_begin_vnode_operation(&fc, dvnode, key, true)) {
dir_data_version = dvnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
afs_fs_rename(&fc, old->d_name.name,
dvnode, new->d_name.name,
scb, scb);
}
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
&dir_data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
if (ret == 0) {
spin_lock(&old->d_lock);
old->d_flags |= DCACHE_NFSFS_RENAMED;
spin_unlock(&old->d_lock);
if (dvnode->silly_key != key) {
key_put(dvnode->silly_key);
dvnode->silly_key = key_get(key);
}
down_write(&dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
dvnode->status.data_version == dir_data_version) {
afs_edit_dir_remove(dvnode, &old->d_name,
afs_edit_dir_for_silly_0);
afs_edit_dir_add(dvnode, &new->d_name,
&vnode->fid, afs_edit_dir_for_silly_1);
}
up_write(&dvnode->validate_lock);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
return afs_do_sync_operation(op);
}
/**
@ -139,65 +146,66 @@ out:
return ret;
}
static void afs_silly_unlink_success(struct afs_operation *op)
{
struct afs_vnode *vnode = op->file[1].vnode;
_enter("op=%08x", op->debug_id);
afs_check_for_remote_deletion(op, op->file[0].vnode);
afs_vnode_commit_status(op, &op->file[0]);
afs_vnode_commit_status(op, &op->file[1]);
afs_update_dentry_version(op, &op->file[0], op->dentry);
drop_nlink(&vnode->vfs_inode);
if (vnode->vfs_inode.i_nlink == 0) {
set_bit(AFS_VNODE_DELETED, &vnode->flags);
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
}
}
static void afs_silly_unlink_edit_dir(struct afs_operation *op)
{
struct afs_vnode_param *dvp = &op->file[0];
struct afs_vnode *dvnode = dvp->vnode;
_enter("op=%08x", op->debug_id);
down_write(&dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
dvnode->status.data_version == dvp->dv_before + dvp->dv_delta)
afs_edit_dir_remove(dvnode, &op->dentry->d_name,
afs_edit_dir_for_unlink);
up_write(&dvnode->validate_lock);
}
static const struct afs_operation_ops afs_silly_unlink_operation = {
.issue_afs_rpc = afs_fs_remove_file,
.issue_yfs_rpc = yfs_fs_remove_file,
.success = afs_silly_unlink_success,
.edit_dir = afs_silly_unlink_edit_dir,
};
/*
* Tell the server to remove a sillyrename file.
*/
static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode,
struct dentry *dentry, struct key *key)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
int ret = -ERESTARTSYS;
struct afs_operation *op;
_enter("");
scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
op = afs_alloc_operation(NULL, dvnode->volume);
if (IS_ERR(op))
return PTR_ERR(op);
afs_op_set_vnode(op, 0, dvnode);
afs_op_set_vnode(op, 1, vnode);
op->dentry = dentry;
op->ops = &afs_silly_unlink_operation;
trace_afs_silly_rename(vnode, true);
if (afs_begin_vnode_operation(&fc, dvnode, key, false)) {
afs_dataversion_t dir_data_version = dvnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(dvnode);
if (test_bit(AFS_SERVER_FL_IS_YFS, &fc.cbi->server->flags) &&
!test_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags)) {
yfs_fs_remove_file2(&fc, vnode, dentry->d_name.name,
&scb[0], &scb[1]);
if (fc.ac.error != -ECONNABORTED ||
fc.ac.abort_code != RXGEN_OPCODE)
continue;
set_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags);
}
afs_fs_remove(&fc, vnode, dentry->d_name.name, false, &scb[0]);
}
afs_vnode_commit_status(&fc, dvnode, fc.cb_break,
&dir_data_version, &scb[0]);
ret = afs_end_vnode_operation(&fc);
if (ret == 0) {
drop_nlink(&vnode->vfs_inode);
if (vnode->vfs_inode.i_nlink == 0) {
set_bit(AFS_VNODE_DELETED, &vnode->flags);
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
}
}
if (ret == 0) {
down_write(&dvnode->validate_lock);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) &&
dvnode->status.data_version == dir_data_version)
afs_edit_dir_remove(dvnode, &dentry->d_name,
afs_edit_dir_for_unlink);
up_write(&dvnode->validate_lock);
}
}
kfree(scb);
_leave(" = %d", ret);
return ret;
return afs_do_sync_operation(op);
}
/*

Просмотреть файл

@ -10,6 +10,99 @@
#include <linux/dns_resolver.h>
#include "internal.h"
static atomic_t afs_autocell_ino;
/*
* iget5() comparator for inode created by autocell operations
*
* These pseudo inodes don't match anything.
*/
static int afs_iget5_pseudo_test(struct inode *inode, void *opaque)
{
return 0;
}
/*
* iget5() inode initialiser
*/
static int afs_iget5_pseudo_set(struct inode *inode, void *opaque)
{
struct afs_super_info *as = AFS_FS_S(inode->i_sb);
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_fid *fid = opaque;
vnode->volume = as->volume;
vnode->fid = *fid;
inode->i_ino = fid->vnode;
inode->i_generation = fid->unique;
return 0;
}
/*
* Create an inode for a dynamic root directory or an autocell dynamic
* automount dir.
*/
struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root)
{
struct afs_super_info *as = AFS_FS_S(sb);
struct afs_vnode *vnode;
struct inode *inode;
struct afs_fid fid = {};
_enter("");
if (as->volume)
fid.vid = as->volume->vid;
if (root) {
fid.vnode = 1;
fid.unique = 1;
} else {
fid.vnode = atomic_inc_return(&afs_autocell_ino);
fid.unique = 0;
}
inode = iget5_locked(sb, fid.vnode,
afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid);
if (!inode) {
_leave(" = -ENOMEM");
return ERR_PTR(-ENOMEM);
}
_debug("GOT INODE %p { ino=%lu, vl=%llx, vn=%llx, u=%x }",
inode, inode->i_ino, fid.vid, fid.vnode, fid.unique);
vnode = AFS_FS_I(inode);
/* there shouldn't be an existing inode */
BUG_ON(!(inode->i_state & I_NEW));
inode->i_size = 0;
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
if (root) {
inode->i_op = &afs_dynroot_inode_operations;
inode->i_fop = &simple_dir_operations;
} else {
inode->i_op = &afs_autocell_inode_operations;
}
set_nlink(inode, 2);
inode->i_uid = GLOBAL_ROOT_UID;
inode->i_gid = GLOBAL_ROOT_GID;
inode->i_ctime = inode->i_atime = inode->i_mtime = current_time(inode);
inode->i_blocks = 0;
inode->i_generation = 0;
set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
if (!root) {
set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
inode->i_flags |= S_AUTOMOUNT;
}
inode->i_flags |= S_NOATIME;
unlock_new_inode(inode);
_leave(" = %p", inode);
return inode;
}
/*
* Probe to see if a cell may exist. This prevents positive dentries from
* being created unnecessarily.

Просмотреть файл

@ -69,7 +69,7 @@ static const struct vm_operations_struct afs_vm_ops = {
*/
void afs_put_wb_key(struct afs_wb_key *wbk)
{
if (refcount_dec_and_test(&wbk->usage)) {
if (wbk && refcount_dec_and_test(&wbk->usage)) {
key_put(wbk->key);
kfree(wbk);
}
@ -220,14 +220,35 @@ static void afs_file_readpage_read_complete(struct page *page,
}
#endif
static void afs_fetch_data_success(struct afs_operation *op)
{
struct afs_vnode *vnode = op->file[0].vnode;
_enter("op=%08x", op->debug_id);
afs_check_for_remote_deletion(op, vnode);
afs_vnode_commit_status(op, &op->file[0]);
afs_stat_v(vnode, n_fetches);
atomic_long_add(op->fetch.req->actual_len, &op->net->n_fetch_bytes);
}
static void afs_fetch_data_put(struct afs_operation *op)
{
afs_put_read(op->fetch.req);
}
static const struct afs_operation_ops afs_fetch_data_operation = {
.issue_afs_rpc = afs_fs_fetch_data,
.issue_yfs_rpc = yfs_fs_fetch_data,
.success = afs_fetch_data_success,
.put = afs_fetch_data_put,
};
/*
* Fetch file data from the volume.
*/
int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *req)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
int ret;
struct afs_operation *op;
_enter("%s{%llx:%llu.%u},%x,,,",
vnode->volume->name,
@ -236,34 +257,15 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *re
vnode->fid.unique,
key_serial(key));
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
op = afs_alloc_operation(key, vnode->volume);
if (IS_ERR(op))
return PTR_ERR(op);
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
afs_op_set_vnode(op, 0, vnode);
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_fetch_data(&fc, scb, req);
}
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
if (ret == 0) {
afs_stat_v(vnode, n_fetches);
atomic_long_add(req->actual_len,
&afs_v2net(vnode)->n_fetch_bytes);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
op->fetch.req = afs_get_read(req);
op->ops = &afs_fetch_data_operation;
return afs_do_sync_operation(op);
}
/*

Просмотреть файл

@ -70,7 +70,8 @@ static void afs_schedule_lock_extension(struct afs_vnode *vnode)
*/
void afs_lock_op_done(struct afs_call *call)
{
struct afs_vnode *vnode = call->lvnode;
struct afs_operation *op = call->op;
struct afs_vnode *vnode = op->lock.lvnode;
if (call->error == 0) {
spin_lock(&vnode->lock);
@ -172,15 +173,28 @@ static void afs_kill_lockers_enoent(struct afs_vnode *vnode)
vnode->lock_key = NULL;
}
static void afs_lock_success(struct afs_operation *op)
{
struct afs_vnode *vnode = op->file[0].vnode;
_enter("op=%08x", op->debug_id);
afs_check_for_remote_deletion(op, vnode);
afs_vnode_commit_status(op, &op->file[0]);
}
static const struct afs_operation_ops afs_set_lock_operation = {
.issue_afs_rpc = afs_fs_set_lock,
.issue_yfs_rpc = yfs_fs_set_lock,
.success = afs_lock_success,
};
/*
* Get a lock on a file
*/
static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
afs_lock_type_t type)
{
struct afs_status_cb *scb;
struct afs_fs_cursor fc;
int ret;
struct afs_operation *op;
_enter("%s{%llx:%llu.%u},%x,%u",
vnode->volume->name,
@ -189,35 +203,29 @@ static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
vnode->fid.unique,
key_serial(key), type);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
op = afs_alloc_operation(key, vnode->volume);
if (IS_ERR(op))
return PTR_ERR(op);
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_set_lock(&fc, type, scb);
}
afs_op_set_vnode(op, 0, vnode);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
ret = afs_end_vnode_operation(&fc);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
op->lock.type = type;
op->ops = &afs_set_lock_operation;
return afs_do_sync_operation(op);
}
static const struct afs_operation_ops afs_extend_lock_operation = {
.issue_afs_rpc = afs_fs_extend_lock,
.issue_yfs_rpc = yfs_fs_extend_lock,
.success = afs_lock_success,
};
/*
* Extend a lock on a file
*/
static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
{
struct afs_status_cb *scb;
struct afs_fs_cursor fc;
int ret;
struct afs_operation *op;
_enter("%s{%llx:%llu.%u},%x",
vnode->volume->name,
@ -226,35 +234,29 @@ static int afs_extend_lock(struct afs_vnode *vnode, struct key *key)
vnode->fid.unique,
key_serial(key));
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
op = afs_alloc_operation(key, vnode->volume);
if (IS_ERR(op))
return PTR_ERR(op);
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, false)) {
while (afs_select_current_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_extend_lock(&fc, scb);
}
afs_op_set_vnode(op, 0, vnode);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
ret = afs_end_vnode_operation(&fc);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
op->flags |= AFS_OPERATION_UNINTR;
op->ops = &afs_extend_lock_operation;
return afs_do_sync_operation(op);
}
static const struct afs_operation_ops afs_release_lock_operation = {
.issue_afs_rpc = afs_fs_release_lock,
.issue_yfs_rpc = yfs_fs_release_lock,
.success = afs_lock_success,
};
/*
* Release a lock on a file
*/
static int afs_release_lock(struct afs_vnode *vnode, struct key *key)
{
struct afs_status_cb *scb;
struct afs_fs_cursor fc;
int ret;
struct afs_operation *op;
_enter("%s{%llx:%llu.%u},%x",
vnode->volume->name,
@ -263,25 +265,15 @@ static int afs_release_lock(struct afs_vnode *vnode, struct key *key)
vnode->fid.unique,
key_serial(key));
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
op = afs_alloc_operation(key, vnode->volume);
if (IS_ERR(op))
return PTR_ERR(op);
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, false)) {
while (afs_select_current_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_release_lock(&fc, scb);
}
afs_op_set_vnode(op, 0, vnode);
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break, NULL, scb);
ret = afs_end_vnode_operation(&fc);
}
kfree(scb);
_leave(" = %d", ret);
return ret;
op->flags |= AFS_OPERATION_UNINTR;
op->ops = &afs_release_lock_operation;
return afs_do_sync_operation(op);
}
/*

239
fs/afs/fs_operation.c Normal file
Просмотреть файл

@ -0,0 +1,239 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Fileserver-directed operation handling.
*
* Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include "internal.h"
static atomic_t afs_operation_debug_counter;
/*
* Create an operation against a volume.
*/
struct afs_operation *afs_alloc_operation(struct key *key, struct afs_volume *volume)
{
struct afs_operation *op;
_enter("");
op = kzalloc(sizeof(*op), GFP_KERNEL);
if (!op)
return ERR_PTR(-ENOMEM);
if (!key) {
key = afs_request_key(volume->cell);
if (IS_ERR(key)) {
kfree(op);
return ERR_CAST(key);
}
} else {
key_get(key);
}
op->key = key;
op->volume = afs_get_volume(volume, afs_volume_trace_get_new_op);
op->net = volume->cell->net;
op->cb_v_break = volume->cb_v_break;
op->debug_id = atomic_inc_return(&afs_operation_debug_counter);
op->error = -EDESTADDRREQ;
op->ac.error = SHRT_MAX;
_leave(" = [op=%08x]", op->debug_id);
return op;
}
/*
* Lock the vnode(s) being operated upon.
*/
static bool afs_get_io_locks(struct afs_operation *op)
{
struct afs_vnode *vnode = op->file[0].vnode;
struct afs_vnode *vnode2 = op->file[1].vnode;
_enter("");
if (op->flags & AFS_OPERATION_UNINTR) {
mutex_lock(&vnode->io_lock);
op->flags |= AFS_OPERATION_LOCK_0;
_leave(" = t [1]");
return true;
}
if (!vnode2 || !op->file[1].need_io_lock || vnode == vnode2)
vnode2 = NULL;
if (vnode2 > vnode)
swap(vnode, vnode2);
if (mutex_lock_interruptible(&vnode->io_lock) < 0) {
op->error = -EINTR;
op->flags |= AFS_OPERATION_STOP;
_leave(" = f [I 0]");
return false;
}
op->flags |= AFS_OPERATION_LOCK_0;
if (vnode2) {
if (mutex_lock_interruptible_nested(&vnode2->io_lock, 1) < 0) {
op->error = -EINTR;
op->flags |= AFS_OPERATION_STOP;
mutex_unlock(&vnode->io_lock);
op->flags &= ~AFS_OPERATION_LOCK_0;
_leave(" = f [I 1]");
return false;
}
op->flags |= AFS_OPERATION_LOCK_1;
}
_leave(" = t [2]");
return true;
}
static void afs_drop_io_locks(struct afs_operation *op)
{
struct afs_vnode *vnode = op->file[0].vnode;
struct afs_vnode *vnode2 = op->file[1].vnode;
_enter("");
if (op->flags & AFS_OPERATION_LOCK_1)
mutex_unlock(&vnode2->io_lock);
if (op->flags & AFS_OPERATION_LOCK_0)
mutex_unlock(&vnode->io_lock);
}
static void afs_prepare_vnode(struct afs_operation *op, struct afs_vnode_param *vp,
unsigned int index)
{
struct afs_vnode *vnode = vp->vnode;
if (vnode) {
vp->fid = vnode->fid;
vp->dv_before = vnode->status.data_version;
vp->cb_break_before = afs_calc_vnode_cb_break(vnode);
if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
op->flags |= AFS_OPERATION_CUR_ONLY;
}
if (vp->fid.vnode)
_debug("PREP[%u] {%llx:%llu.%u}",
index, vp->fid.vid, vp->fid.vnode, vp->fid.unique);
}
/*
* Begin an operation on the fileserver.
*
* Fileserver operations are serialised on the server by vnode, so we serialise
* them here also using the io_lock.
*/
bool afs_begin_vnode_operation(struct afs_operation *op)
{
struct afs_vnode *vnode = op->file[0].vnode;
ASSERT(vnode);
_enter("");
if (op->file[0].need_io_lock)
if (!afs_get_io_locks(op))
return false;
afs_prepare_vnode(op, &op->file[0], 0);
afs_prepare_vnode(op, &op->file[1], 1);
op->cb_v_break = op->volume->cb_v_break;
_leave(" = true");
return true;
}
/*
* Tidy up a filesystem cursor and unlock the vnode.
*/
static void afs_end_vnode_operation(struct afs_operation *op)
{
_enter("");
if (op->error == -EDESTADDRREQ ||
op->error == -EADDRNOTAVAIL ||
op->error == -ENETUNREACH ||
op->error == -EHOSTUNREACH)
afs_dump_edestaddrreq(op);
afs_drop_io_locks(op);
if (op->error == -ECONNABORTED)
op->error = afs_abort_to_error(op->ac.abort_code);
}
/*
* Wait for an in-progress operation to complete.
*/
void afs_wait_for_operation(struct afs_operation *op)
{
_enter("");
while (afs_select_fileserver(op)) {
op->cb_s_break = op->server->cb_s_break;
if (test_bit(AFS_SERVER_FL_IS_YFS, &op->server->flags) &&
op->ops->issue_yfs_rpc)
op->ops->issue_yfs_rpc(op);
else
op->ops->issue_afs_rpc(op);
op->error = afs_wait_for_call_to_complete(op->call, &op->ac);
}
if (op->error == 0) {
_debug("success");
op->ops->success(op);
}
afs_end_vnode_operation(op);
if (op->error == 0 && op->ops->edit_dir) {
_debug("edit_dir");
op->ops->edit_dir(op);
}
_leave("");
}
/*
* Dispose of an operation.
*/
int afs_put_operation(struct afs_operation *op)
{
int i, ret = op->error;
_enter("op=%08x,%d", op->debug_id, ret);
if (op->ops && op->ops->put)
op->ops->put(op);
if (op->file[0].put_vnode)
iput(&op->file[0].vnode->vfs_inode);
if (op->file[1].put_vnode)
iput(&op->file[1].vnode->vfs_inode);
if (op->more_files) {
for (i = 0; i < op->nr_files - 2; i++)
if (op->more_files[i].put_vnode)
iput(&op->more_files[i].vnode->vfs_inode);
kfree(op->more_files);
}
afs_end_cursor(&op->ac);
afs_put_serverlist(op->net, op->server_list);
afs_put_volume(op->net, op->volume, afs_volume_trace_put_put_op);
kfree(op);
return ret;
}
int afs_do_sync_operation(struct afs_operation *op)
{
afs_begin_vnode_operation(op);
afs_wait_for_operation(op);
return afs_put_operation(op);
}

Просмотреть файл

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* AFS fileserver probing
*
* Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
* Copyright (C) 2018, 2020 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
@ -11,15 +11,86 @@
#include "internal.h"
#include "protocol_yfs.h"
static bool afs_fs_probe_done(struct afs_server *server)
{
if (!atomic_dec_and_test(&server->probe_outstanding))
return false;
static unsigned int afs_fs_probe_fast_poll_interval = 30 * HZ;
static unsigned int afs_fs_probe_slow_poll_interval = 5 * 60 * HZ;
wake_up_var(&server->probe_outstanding);
clear_bit_unlock(AFS_SERVER_FL_PROBING, &server->flags);
wake_up_bit(&server->flags, AFS_SERVER_FL_PROBING);
return true;
/*
* Start the probe polling timer. We have to supply it with an inc on the
* outstanding server count.
*/
static void afs_schedule_fs_probe(struct afs_net *net,
struct afs_server *server, bool fast)
{
unsigned long atj;
if (!net->live)
return;
atj = server->probed_at;
atj += fast ? afs_fs_probe_fast_poll_interval : afs_fs_probe_slow_poll_interval;
afs_inc_servers_outstanding(net);
if (timer_reduce(&net->fs_probe_timer, atj))
afs_dec_servers_outstanding(net);
}
/*
* Handle the completion of a set of probes.
*/
static void afs_finished_fs_probe(struct afs_net *net, struct afs_server *server)
{
bool responded = server->probe.responded;
write_seqlock(&net->fs_lock);
if (responded) {
list_add_tail(&server->probe_link, &net->fs_probe_slow);
} else {
server->rtt = UINT_MAX;
clear_bit(AFS_SERVER_FL_RESPONDING, &server->flags);
list_add_tail(&server->probe_link, &net->fs_probe_fast);
}
write_sequnlock(&net->fs_lock);
afs_schedule_fs_probe(net, server, !responded);
}
/*
* Handle the completion of a probe.
*/
static void afs_done_one_fs_probe(struct afs_net *net, struct afs_server *server)
{
_enter("");
if (atomic_dec_and_test(&server->probe_outstanding))
afs_finished_fs_probe(net, server);
wake_up_all(&server->probe_wq);
}
/*
* Handle inability to send a probe due to ENOMEM when trying to allocate a
* call struct.
*/
static void afs_fs_probe_not_done(struct afs_net *net,
struct afs_server *server,
struct afs_addr_cursor *ac)
{
struct afs_addr_list *alist = ac->alist;
unsigned int index = ac->index;
_enter("");
trace_afs_io_error(0, -ENOMEM, afs_io_error_fs_probe_fail);
spin_lock(&server->probe_lock);
server->probe.local_failure = true;
if (server->probe.error == 0)
server->probe.error = -ENOMEM;
set_bit(index, &alist->failed);
spin_unlock(&server->probe_lock);
return afs_done_one_fs_probe(net, server);
}
/*
@ -30,10 +101,8 @@ void afs_fileserver_probe_result(struct afs_call *call)
{
struct afs_addr_list *alist = call->alist;
struct afs_server *server = call->server;
unsigned int server_index = call->server_index;
unsigned int index = call->addr_ix;
unsigned int rtt_us = 0;
bool have_result = false;
int ret = call->error;
_enter("%pU,%u", &server->uuid, index);
@ -52,8 +121,9 @@ void afs_fileserver_probe_result(struct afs_call *call)
goto responded;
case -ENOMEM:
case -ENONET:
clear_bit(index, &alist->responded);
server->probe.local_failure = true;
afs_io_error(call, afs_io_error_fs_probe_fail);
trace_afs_io_error(call->debug_id, ret, afs_io_error_fs_probe_fail);
goto out;
case -ECONNRESET: /* Responded, but call expired. */
case -ERFKILL:
@ -72,12 +142,11 @@ void afs_fileserver_probe_result(struct afs_call *call)
server->probe.error == -ETIMEDOUT ||
server->probe.error == -ETIME))
server->probe.error = ret;
afs_io_error(call, afs_io_error_fs_probe_fail);
trace_afs_io_error(call->debug_id, ret, afs_io_error_fs_probe_fail);
goto out;
}
responded:
set_bit(index, &alist->responded);
clear_bit(index, &alist->failed);
if (call->service_id == YFS_FS_SERVICE) {
@ -95,39 +164,34 @@ responded:
rtt_us = rxrpc_kernel_get_srtt(call->net->socket, call->rxcall);
if (rtt_us < server->probe.rtt) {
server->probe.rtt = rtt_us;
server->rtt = rtt_us;
alist->preferred = index;
have_result = true;
}
smp_wmb(); /* Set rtt before responded. */
server->probe.responded = true;
set_bit(AFS_SERVER_FL_PROBED, &server->flags);
set_bit(index, &alist->responded);
set_bit(AFS_SERVER_FL_RESPONDING, &server->flags);
out:
spin_unlock(&server->probe_lock);
_debug("probe [%u][%u] %pISpc rtt=%u ret=%d",
server_index, index, &alist->addrs[index].transport, rtt_us, ret);
_debug("probe %pU [%u] %pISpc rtt=%u ret=%d",
&server->uuid, index, &alist->addrs[index].transport,
rtt_us, ret);
have_result |= afs_fs_probe_done(server);
if (have_result)
wake_up_all(&server->probe_wq);
return afs_done_one_fs_probe(call->net, server);
}
/*
* Probe all of a fileserver's addresses to find out the best route and to
* query its capabilities.
* Probe one or all of a fileserver's addresses to find out the best route and
* to query its capabilities.
*/
static int afs_do_probe_fileserver(struct afs_net *net,
struct afs_server *server,
struct key *key,
unsigned int server_index,
struct afs_error *_e)
void afs_fs_probe_fileserver(struct afs_net *net, struct afs_server *server,
struct key *key, bool all)
{
struct afs_addr_cursor ac = {
.index = 0,
};
struct afs_call *call;
bool in_progress = false;
_enter("%pU", &server->uuid);
@ -137,50 +201,25 @@ static int afs_do_probe_fileserver(struct afs_net *net,
afs_get_addrlist(ac.alist);
read_unlock(&server->fs_lock);
atomic_set(&server->probe_outstanding, ac.alist->nr_addrs);
server->probed_at = jiffies;
atomic_set(&server->probe_outstanding, all ? ac.alist->nr_addrs : 1);
memset(&server->probe, 0, sizeof(server->probe));
server->probe.rtt = UINT_MAX;
for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++) {
call = afs_fs_get_capabilities(net, server, &ac, key, server_index);
if (!IS_ERR(call)) {
afs_put_call(call);
in_progress = true;
} else {
afs_prioritise_error(_e, PTR_ERR(call), ac.abort_code);
}
ac.index = ac.alist->preferred;
if (ac.index < 0 || ac.index >= ac.alist->nr_addrs)
all = true;
if (all) {
for (ac.index = 0; ac.index < ac.alist->nr_addrs; ac.index++)
if (!afs_fs_get_capabilities(net, server, &ac, key))
afs_fs_probe_not_done(net, server, &ac);
} else {
if (!afs_fs_get_capabilities(net, server, &ac, key))
afs_fs_probe_not_done(net, server, &ac);
}
if (!in_progress)
afs_fs_probe_done(server);
afs_put_addrlist(ac.alist);
return in_progress;
}
/*
* Send off probes to all unprobed servers.
*/
int afs_probe_fileservers(struct afs_net *net, struct key *key,
struct afs_server_list *list)
{
struct afs_server *server;
struct afs_error e;
bool in_progress = false;
int i;
e.error = 0;
e.responded = false;
for (i = 0; i < list->nr_servers; i++) {
server = list->servers[i].server;
if (test_bit(AFS_SERVER_FL_PROBED, &server->flags))
continue;
if (!test_and_set_bit_lock(AFS_SERVER_FL_PROBING, &server->flags) &&
afs_do_probe_fileserver(net, server, key, i, &e))
in_progress = true;
}
return in_progress ? 0 : e.error;
}
/*
@ -190,7 +229,7 @@ int afs_wait_for_fs_probes(struct afs_server_list *slist, unsigned long untried)
{
struct wait_queue_entry *waits;
struct afs_server *server;
unsigned int rtt = UINT_MAX;
unsigned int rtt = UINT_MAX, rtt_s;
bool have_responders = false;
int pref = -1, i;
@ -200,7 +239,7 @@ int afs_wait_for_fs_probes(struct afs_server_list *slist, unsigned long untried)
for (i = 0; i < slist->nr_servers; i++) {
if (test_bit(i, &untried)) {
server = slist->servers[i].server;
if (!test_bit(AFS_SERVER_FL_PROBING, &server->flags))
if (!atomic_read(&server->probe_outstanding))
__clear_bit(i, &untried);
if (server->probe.responded)
have_responders = true;
@ -230,7 +269,7 @@ int afs_wait_for_fs_probes(struct afs_server_list *slist, unsigned long untried)
server = slist->servers[i].server;
if (server->probe.responded)
goto stop;
if (test_bit(AFS_SERVER_FL_PROBING, &server->flags))
if (atomic_read(&server->probe_outstanding))
still_probing = true;
}
}
@ -246,10 +285,11 @@ stop:
for (i = 0; i < slist->nr_servers; i++) {
if (test_bit(i, &untried)) {
server = slist->servers[i].server;
if (server->probe.responded &&
server->probe.rtt < rtt) {
rtt_s = READ_ONCE(server->rtt);
if (test_bit(AFS_SERVER_FL_RESPONDING, &server->flags) &&
rtt_s < rtt) {
pref = i;
rtt = server->probe.rtt;
rtt = rtt_s;
}
remove_wait_queue(&server->probe_wq, &waits[i]);
@ -265,3 +305,156 @@ stop:
slist->preferred = pref;
return 0;
}
/*
* Probe timer. We have an increment on fs_outstanding that we need to pass
* along to the work item.
*/
void afs_fs_probe_timer(struct timer_list *timer)
{
struct afs_net *net = container_of(timer, struct afs_net, fs_probe_timer);
if (!queue_work(afs_wq, &net->fs_prober))
afs_dec_servers_outstanding(net);
}
/*
* Dispatch a probe to a server.
*/
static void afs_dispatch_fs_probe(struct afs_net *net, struct afs_server *server, bool all)
__releases(&net->fs_lock)
{
struct key *key = NULL;
/* We remove it from the queues here - it will be added back to
* one of the queues on the completion of the probe.
*/
list_del_init(&server->probe_link);
afs_get_server(server, afs_server_trace_get_probe);
write_sequnlock(&net->fs_lock);
afs_fs_probe_fileserver(net, server, key, all);
afs_put_server(net, server, afs_server_trace_put_probe);
}
/*
* Probe a server immediately without waiting for its due time to come
* round. This is used when all of the addresses have been tried.
*/
void afs_probe_fileserver(struct afs_net *net, struct afs_server *server)
{
write_seqlock(&net->fs_lock);
if (!list_empty(&server->probe_link))
return afs_dispatch_fs_probe(net, server, true);
write_sequnlock(&net->fs_lock);
}
/*
* Probe dispatcher to regularly dispatch probes to keep NAT alive.
*/
void afs_fs_probe_dispatcher(struct work_struct *work)
{
struct afs_net *net = container_of(work, struct afs_net, fs_prober);
struct afs_server *fast, *slow, *server;
unsigned long nowj, timer_at, poll_at;
bool first_pass = true, set_timer = false;
if (!net->live)
return;
_enter("");
if (list_empty(&net->fs_probe_fast) && list_empty(&net->fs_probe_slow)) {
_leave(" [none]");
return;
}
again:
write_seqlock(&net->fs_lock);
fast = slow = server = NULL;
nowj = jiffies;
timer_at = nowj + MAX_JIFFY_OFFSET;
if (!list_empty(&net->fs_probe_fast)) {
fast = list_first_entry(&net->fs_probe_fast, struct afs_server, probe_link);
poll_at = fast->probed_at + afs_fs_probe_fast_poll_interval;
if (time_before(nowj, poll_at)) {
timer_at = poll_at;
set_timer = true;
fast = NULL;
}
}
if (!list_empty(&net->fs_probe_slow)) {
slow = list_first_entry(&net->fs_probe_slow, struct afs_server, probe_link);
poll_at = slow->probed_at + afs_fs_probe_slow_poll_interval;
if (time_before(nowj, poll_at)) {
if (time_before(poll_at, timer_at))
timer_at = poll_at;
set_timer = true;
slow = NULL;
}
}
server = fast ?: slow;
if (server)
_debug("probe %pU", &server->uuid);
if (server && (first_pass || !need_resched())) {
afs_dispatch_fs_probe(net, server, server == fast);
first_pass = false;
goto again;
}
write_sequnlock(&net->fs_lock);
if (server) {
if (!queue_work(afs_wq, &net->fs_prober))
afs_dec_servers_outstanding(net);
_leave(" [requeue]");
} else if (set_timer) {
if (timer_reduce(&net->fs_probe_timer, timer_at))
afs_dec_servers_outstanding(net);
_leave(" [timer]");
} else {
afs_dec_servers_outstanding(net);
_leave(" [quiesce]");
}
}
/*
* Wait for a probe on a particular fileserver to complete for 2s.
*/
int afs_wait_for_one_fs_probe(struct afs_server *server, bool is_intr)
{
struct wait_queue_entry wait;
unsigned long timo = 2 * HZ;
if (atomic_read(&server->probe_outstanding) == 0)
goto dont_wait;
init_wait_entry(&wait, 0);
for (;;) {
prepare_to_wait_event(&server->probe_wq, &wait,
is_intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
if (timo == 0 ||
server->probe.responded ||
atomic_read(&server->probe_outstanding) == 0 ||
(is_intr && signal_pending(current)))
break;
timo = schedule_timeout(timo);
}
finish_wait(&server->probe_wq, &wait);
dont_wait:
if (server->probe.responded)
return 0;
if (is_intr && signal_pending(current))
return -ERESTARTSYS;
if (timo == 0)
return -ETIME;
return -EDESTADDRREQ;
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -67,16 +67,18 @@ static void afs_set_i_size(struct afs_vnode *vnode, u64 size)
/*
* Initialise an inode from the vnode status.
*/
static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
struct afs_cb_interest *cbi,
struct afs_vnode *parent_vnode,
struct afs_status_cb *scb)
static int afs_inode_init_from_status(struct afs_operation *op,
struct afs_vnode_param *vp,
struct afs_vnode *vnode)
{
struct afs_cb_interest *old_cbi = NULL;
struct afs_file_status *status = &scb->status;
struct afs_file_status *status = &vp->scb.status;
struct inode *inode = AFS_VNODE_TO_I(vnode);
struct timespec64 t;
_enter("{%llx:%llu.%u} %s",
vp->fid.vid, vp->fid.vnode, vp->fid.unique,
op->type ? op->type->name : "???");
_debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu",
status->type,
status->nlink,
@ -86,12 +88,15 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
write_seqlock(&vnode->cb_lock);
vnode->cb_v_break = op->cb_v_break;
vnode->cb_s_break = op->cb_s_break;
vnode->status = *status;
t = status->mtime_client;
inode->i_ctime = t;
inode->i_mtime = t;
inode->i_atime = t;
inode->i_flags |= S_NOATIME;
inode->i_uid = make_kuid(&init_user_ns, status->owner);
inode->i_gid = make_kgid(&init_user_ns, status->group);
set_nlink(&vnode->vfs_inode, status->nlink);
@ -128,9 +133,9 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
inode_nohighmem(inode);
break;
default:
dump_vnode(vnode, parent_vnode);
dump_vnode(vnode, op->file[0].vnode != vnode ? op->file[0].vnode : NULL);
write_sequnlock(&vnode->cb_lock);
return afs_protocol_error(NULL, -EBADMSG, afs_eproto_file_type);
return afs_protocol_error(NULL, afs_eproto_file_type);
}
afs_set_i_size(vnode, status->size);
@ -138,39 +143,36 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key,
vnode->invalid_before = status->data_version;
inode_set_iversion_raw(&vnode->vfs_inode, status->data_version);
if (!scb->have_cb) {
if (!vp->scb.have_cb) {
/* it's a symlink we just created (the fileserver
* didn't give us a callback) */
vnode->cb_expires_at = ktime_get_real_seconds();
} else {
vnode->cb_expires_at = scb->callback.expires_at;
old_cbi = rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->cb_lock.lock));
if (cbi != old_cbi)
rcu_assign_pointer(vnode->cb_interest, afs_get_cb_interest(cbi));
else
old_cbi = NULL;
vnode->cb_expires_at = vp->scb.callback.expires_at;
vnode->cb_server = op->server;
set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
}
write_sequnlock(&vnode->cb_lock);
afs_put_cb_interest(afs_v2net(vnode), old_cbi);
return 0;
}
/*
* Update the core inode struct from a returned status record.
*/
static void afs_apply_status(struct afs_fs_cursor *fc,
struct afs_vnode *vnode,
struct afs_status_cb *scb,
const afs_dataversion_t *expected_version)
static void afs_apply_status(struct afs_operation *op,
struct afs_vnode_param *vp)
{
struct afs_file_status *status = &scb->status;
struct afs_file_status *status = &vp->scb.status;
struct afs_vnode *vnode = vp->vnode;
struct timespec64 t;
umode_t mode;
bool data_changed = false;
_enter("{%llx:%llu.%u} %s",
vp->fid.vid, vp->fid.vnode, vp->fid.unique,
op->type ? op->type->name : "???");
BUG_ON(test_bit(AFS_VNODE_UNSET, &vnode->flags));
if (status->type != vnode->status.type) {
@ -179,7 +181,7 @@ static void afs_apply_status(struct afs_fs_cursor *fc,
vnode->fid.vnode,
vnode->fid.unique,
status->type, vnode->status.type);
afs_protocol_error(NULL, -EBADMSG, afs_eproto_bad_status);
afs_protocol_error(NULL, afs_eproto_bad_status);
return;
}
@ -209,14 +211,13 @@ static void afs_apply_status(struct afs_fs_cursor *fc,
vnode->status = *status;
if (expected_version &&
*expected_version != status->data_version) {
if (vp->dv_before + vp->dv_delta != status->data_version) {
if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags))
pr_warn("kAFS: vnode modified {%llx:%llu} %llx->%llx %s\n",
vnode->fid.vid, vnode->fid.vnode,
(unsigned long long)*expected_version,
(unsigned long long)vp->dv_before + vp->dv_delta,
(unsigned long long)status->data_version,
fc->type ? fc->type->name : "???");
op->type ? op->type->name : "???");
vnode->invalid_before = status->data_version;
if (vnode->status.type == AFS_FTYPE_DIR) {
@ -243,22 +244,15 @@ static void afs_apply_status(struct afs_fs_cursor *fc,
/*
* Apply a callback to a vnode.
*/
static void afs_apply_callback(struct afs_fs_cursor *fc,
struct afs_vnode *vnode,
struct afs_status_cb *scb,
unsigned int cb_break)
static void afs_apply_callback(struct afs_operation *op,
struct afs_vnode_param *vp)
{
struct afs_cb_interest *old;
struct afs_callback *cb = &scb->callback;
struct afs_callback *cb = &vp->scb.callback;
struct afs_vnode *vnode = vp->vnode;
if (!afs_cb_is_broken(cb_break, vnode, fc->cbi)) {
if (!afs_cb_is_broken(vp->cb_break_before, vnode)) {
vnode->cb_expires_at = cb->expires_at;
old = rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->cb_lock.lock));
if (old != fc->cbi) {
rcu_assign_pointer(vnode->cb_interest, afs_get_cb_interest(fc->cbi));
afs_put_cb_interest(afs_v2net(vnode), old);
}
vnode->cb_server = op->server;
set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
}
}
@ -267,106 +261,108 @@ static void afs_apply_callback(struct afs_fs_cursor *fc,
* Apply the received status and callback to an inode all in the same critical
* section to avoid races with afs_validate().
*/
void afs_vnode_commit_status(struct afs_fs_cursor *fc,
struct afs_vnode *vnode,
unsigned int cb_break,
const afs_dataversion_t *expected_version,
struct afs_status_cb *scb)
void afs_vnode_commit_status(struct afs_operation *op, struct afs_vnode_param *vp)
{
if (fc->ac.error != 0)
return;
struct afs_vnode *vnode = vp->vnode;
_enter("");
ASSERTCMP(op->error, ==, 0);
write_seqlock(&vnode->cb_lock);
if (scb->have_error) {
if (scb->status.abort_code == VNOVNODE) {
if (vp->scb.have_error) {
if (vp->scb.status.abort_code == VNOVNODE) {
set_bit(AFS_VNODE_DELETED, &vnode->flags);
clear_nlink(&vnode->vfs_inode);
__afs_break_callback(vnode, afs_cb_break_for_deleted);
}
} else {
if (scb->have_status)
afs_apply_status(fc, vnode, scb, expected_version);
if (scb->have_cb)
afs_apply_callback(fc, vnode, scb, cb_break);
if (vp->scb.have_status)
afs_apply_status(op, vp);
if (vp->scb.have_cb)
afs_apply_callback(op, vp);
}
write_sequnlock(&vnode->cb_lock);
if (fc->ac.error == 0 && scb->have_status)
afs_cache_permit(vnode, fc->key, cb_break, scb);
if (op->error == 0 && vp->scb.have_status)
afs_cache_permit(vnode, op->key, vp->cb_break_before, &vp->scb);
}
static void afs_fetch_status_success(struct afs_operation *op)
{
struct afs_vnode_param *vp = &op->file[0];
struct afs_vnode *vnode = vp->vnode;
int ret;
if (vnode->vfs_inode.i_state & I_NEW) {
ret = afs_inode_init_from_status(op, vp, vnode);
op->error = ret;
if (ret == 0)
afs_cache_permit(vnode, op->key, vp->cb_break_before, &vp->scb);
} else {
afs_vnode_commit_status(op, vp);
}
}
static const struct afs_operation_ops afs_fetch_status_operation = {
.issue_afs_rpc = afs_fs_fetch_status,
.issue_yfs_rpc = yfs_fs_fetch_status,
.success = afs_fetch_status_success,
};
/*
* Fetch file status from the volume.
*/
int afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool is_new,
afs_access_t *_caller_access)
{
struct afs_status_cb *scb;
struct afs_fs_cursor fc;
int ret;
struct afs_operation *op;
_enter("%s,{%llx:%llu.%u,S=%lx}",
vnode->volume->name,
vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique,
vnode->flags);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
return -ENOMEM;
op = afs_alloc_operation(key, vnode->volume);
if (IS_ERR(op))
return PTR_ERR(op);
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
afs_op_set_vnode(op, 0, vnode);
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_fetch_file_status(&fc, scb, NULL);
}
op->nr_files = 1;
op->ops = &afs_fetch_status_operation;
afs_begin_vnode_operation(op);
afs_wait_for_operation(op);
if (fc.error) {
/* Do nothing. */
} else if (is_new) {
ret = afs_inode_init_from_status(vnode, key, fc.cbi,
NULL, scb);
fc.error = ret;
if (ret == 0)
afs_cache_permit(vnode, key, fc.cb_break, scb);
} else {
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
}
afs_check_for_remote_deletion(&fc, vnode);
ret = afs_end_vnode_operation(&fc);
}
if (_caller_access)
*_caller_access = op->file[0].scb.status.caller_access;
return afs_put_operation(op);
}
if (ret == 0 && _caller_access)
*_caller_access = scb->status.caller_access;
kfree(scb);
_leave(" = %d", ret);
return ret;
/*
* ilookup() comparator
*/
int afs_ilookup5_test_by_fid(struct inode *inode, void *opaque)
{
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_fid *fid = opaque;
return (fid->vnode == vnode->fid.vnode &&
fid->vnode_hi == vnode->fid.vnode_hi &&
fid->unique == vnode->fid.unique);
}
/*
* iget5() comparator
*/
int afs_iget5_test(struct inode *inode, void *opaque)
static int afs_iget5_test(struct inode *inode, void *opaque)
{
struct afs_iget_data *iget_data = opaque;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_vnode_param *vp = opaque;
//struct afs_vnode *vnode = AFS_FS_I(inode);
return memcmp(&vnode->fid, &iget_data->fid, sizeof(iget_data->fid)) == 0;
}
/*
* iget5() comparator for inode created by autocell operations
*
* These pseudo inodes don't match anything.
*/
static int afs_iget5_pseudo_dir_test(struct inode *inode, void *opaque)
{
return 0;
return afs_ilookup5_test_by_fid(inode, &vp->fid);
}
/*
@ -374,98 +370,21 @@ static int afs_iget5_pseudo_dir_test(struct inode *inode, void *opaque)
*/
static int afs_iget5_set(struct inode *inode, void *opaque)
{
struct afs_iget_data *iget_data = opaque;
struct afs_vnode_param *vp = opaque;
struct afs_super_info *as = AFS_FS_S(inode->i_sb);
struct afs_vnode *vnode = AFS_FS_I(inode);
vnode->fid = iget_data->fid;
vnode->volume = iget_data->volume;
vnode->cb_v_break = iget_data->cb_v_break;
vnode->cb_s_break = iget_data->cb_s_break;
vnode->volume = as->volume;
vnode->fid = vp->fid;
/* YFS supports 96-bit vnode IDs, but Linux only supports
* 64-bit inode numbers.
*/
inode->i_ino = iget_data->fid.vnode;
inode->i_generation = iget_data->fid.unique;
inode->i_ino = vnode->fid.vnode;
inode->i_generation = vnode->fid.unique;
return 0;
}
/*
* Create an inode for a dynamic root directory or an autocell dynamic
* automount dir.
*/
struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root)
{
struct afs_super_info *as;
struct afs_vnode *vnode;
struct inode *inode;
static atomic_t afs_autocell_ino;
struct afs_iget_data iget_data = {
.cb_v_break = 0,
.cb_s_break = 0,
};
_enter("");
as = sb->s_fs_info;
if (as->volume) {
iget_data.volume = as->volume;
iget_data.fid.vid = as->volume->vid;
}
if (root) {
iget_data.fid.vnode = 1;
iget_data.fid.unique = 1;
} else {
iget_data.fid.vnode = atomic_inc_return(&afs_autocell_ino);
iget_data.fid.unique = 0;
}
inode = iget5_locked(sb, iget_data.fid.vnode,
afs_iget5_pseudo_dir_test, afs_iget5_set,
&iget_data);
if (!inode) {
_leave(" = -ENOMEM");
return ERR_PTR(-ENOMEM);
}
_debug("GOT INODE %p { ino=%lu, vl=%llx, vn=%llx, u=%x }",
inode, inode->i_ino, iget_data.fid.vid, iget_data.fid.vnode,
iget_data.fid.unique);
vnode = AFS_FS_I(inode);
/* there shouldn't be an existing inode */
BUG_ON(!(inode->i_state & I_NEW));
inode->i_size = 0;
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
if (root) {
inode->i_op = &afs_dynroot_inode_operations;
inode->i_fop = &simple_dir_operations;
} else {
inode->i_op = &afs_autocell_inode_operations;
}
set_nlink(inode, 2);
inode->i_uid = GLOBAL_ROOT_UID;
inode->i_gid = GLOBAL_ROOT_GID;
inode->i_ctime = inode->i_atime = inode->i_mtime = current_time(inode);
inode->i_blocks = 0;
inode_set_iversion_raw(inode, 0);
inode->i_generation = 0;
set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
if (!root) {
set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
inode->i_flags |= S_AUTOMOUNT;
}
inode->i_flags |= S_NOATIME;
unlock_new_inode(inode);
_leave(" = %p", inode);
return inode;
}
/*
* Get a cache cookie for an inode.
*/
@ -501,58 +420,41 @@ static void afs_get_inode_cache(struct afs_vnode *vnode)
/*
* inode retrieval
*/
struct inode *afs_iget(struct super_block *sb, struct key *key,
struct afs_iget_data *iget_data,
struct afs_status_cb *scb,
struct afs_cb_interest *cbi,
struct afs_vnode *parent_vnode)
struct inode *afs_iget(struct afs_operation *op, struct afs_vnode_param *vp)
{
struct afs_super_info *as;
struct afs_vnode_param *dvp = &op->file[0];
struct super_block *sb = dvp->vnode->vfs_inode.i_sb;
struct afs_vnode *vnode;
struct afs_fid *fid = &iget_data->fid;
struct inode *inode;
int ret;
_enter(",{%llx:%llu.%u},,", fid->vid, fid->vnode, fid->unique);
_enter(",{%llx:%llu.%u},,", vp->fid.vid, vp->fid.vnode, vp->fid.unique);
as = sb->s_fs_info;
iget_data->volume = as->volume;
inode = iget5_locked(sb, fid->vnode, afs_iget5_test, afs_iget5_set,
iget_data);
inode = iget5_locked(sb, vp->fid.vnode, afs_iget5_test, afs_iget5_set, vp);
if (!inode) {
_leave(" = -ENOMEM");
return ERR_PTR(-ENOMEM);
}
_debug("GOT INODE %p { vl=%llx vn=%llx, u=%x }",
inode, fid->vid, fid->vnode, fid->unique);
vnode = AFS_FS_I(inode);
_debug("GOT INODE %p { vl=%llx vn=%llx, u=%x }",
inode, vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
/* deal with an existing inode */
if (!(inode->i_state & I_NEW)) {
_leave(" = %p", inode);
return inode;
}
if (!scb) {
/* it's a remotely extant inode */
ret = afs_fetch_status(vnode, key, true, NULL);
if (ret < 0)
goto bad_inode;
} else {
ret = afs_inode_init_from_status(vnode, key, cbi, parent_vnode,
scb);
if (ret < 0)
goto bad_inode;
}
ret = afs_inode_init_from_status(op, vp, vnode);
if (ret < 0)
goto bad_inode;
afs_get_inode_cache(vnode);
/* success */
clear_bit(AFS_VNODE_UNSET, &vnode->flags);
inode->i_flags |= S_NOATIME;
unlock_new_inode(inode);
_leave(" = %p", inode);
return inode;
@ -564,6 +466,74 @@ bad_inode:
return ERR_PTR(ret);
}
static int afs_iget5_set_root(struct inode *inode, void *opaque)
{
struct afs_super_info *as = AFS_FS_S(inode->i_sb);
struct afs_vnode *vnode = AFS_FS_I(inode);
vnode->volume = as->volume;
vnode->fid.vid = as->volume->vid,
vnode->fid.vnode = 1;
vnode->fid.unique = 1;
inode->i_ino = 1;
inode->i_generation = 1;
return 0;
}
/*
* Set up the root inode for a volume. This is always vnode 1, unique 1 within
* the volume.
*/
struct inode *afs_root_iget(struct super_block *sb, struct key *key)
{
struct afs_super_info *as = AFS_FS_S(sb);
struct afs_operation *op;
struct afs_vnode *vnode;
struct inode *inode;
int ret;
_enter(",{%llx},,", as->volume->vid);
inode = iget5_locked(sb, 1, NULL, afs_iget5_set_root, NULL);
if (!inode) {
_leave(" = -ENOMEM");
return ERR_PTR(-ENOMEM);
}
_debug("GOT ROOT INODE %p { vl=%llx }", inode, as->volume->vid);
BUG_ON(!(inode->i_state & I_NEW));
vnode = AFS_FS_I(inode);
vnode->cb_v_break = as->volume->cb_v_break,
op = afs_alloc_operation(key, as->volume);
if (IS_ERR(op)) {
ret = PTR_ERR(op);
goto error;
}
afs_op_set_vnode(op, 0, vnode);
op->nr_files = 1;
op->ops = &afs_fetch_status_operation;
ret = afs_do_sync_operation(op);
if (ret < 0)
goto error;
afs_get_inode_cache(vnode);
clear_bit(AFS_VNODE_UNSET, &vnode->flags);
unlock_new_inode(inode);
_leave(" = %p", inode);
return inode;
error:
iget_failed(inode);
_leave(" = %d [bad]", ret);
return ERR_PTR(ret);
}
/*
* mark the data attached to an inode as obsolete due to a write on the server
* - might also want to ditch all the outstanding writes and dirty pages
@ -585,13 +555,31 @@ void afs_zap_data(struct afs_vnode *vnode)
invalidate_inode_pages2(vnode->vfs_inode.i_mapping);
}
/*
* Get the server reinit counter for a vnode's current server.
*/
static bool afs_get_s_break_rcu(struct afs_vnode *vnode, unsigned int *_s_break)
{
struct afs_server_list *slist = rcu_dereference(vnode->volume->servers);
struct afs_server *server;
int i;
for (i = 0; i < slist->nr_servers; i++) {
server = slist->servers[i].server;
if (server == vnode->cb_server) {
*_s_break = READ_ONCE(server->cb_s_break);
return true;
}
}
return false;
}
/*
* Check the validity of a vnode/inode.
*/
bool afs_check_validity(struct afs_vnode *vnode)
{
struct afs_cb_interest *cbi;
struct afs_server *server;
struct afs_volume *volume = vnode->volume;
enum afs_cb_break_reason need_clear = afs_cb_break_no_break;
time64_t now = ktime_get_real_seconds();
@ -604,11 +592,8 @@ bool afs_check_validity(struct afs_vnode *vnode)
cb_v_break = READ_ONCE(volume->cb_v_break);
cb_break = vnode->cb_break;
if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
cbi = rcu_dereference(vnode->cb_interest);
server = rcu_dereference(cbi->server);
cb_s_break = READ_ONCE(server->cb_s_break);
if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags) &&
afs_get_s_break_rcu(vnode, &cb_s_break)) {
if (vnode->cb_s_break != cb_s_break ||
vnode->cb_v_break != cb_v_break) {
vnode->cb_s_break = cb_s_break;
@ -755,7 +740,6 @@ int afs_drop_inode(struct inode *inode)
*/
void afs_evict_inode(struct inode *inode)
{
struct afs_cb_interest *cbi;
struct afs_vnode *vnode;
vnode = AFS_FS_I(inode);
@ -772,15 +756,6 @@ void afs_evict_inode(struct inode *inode)
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
write_seqlock(&vnode->cb_lock);
cbi = rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->cb_lock.lock));
if (cbi) {
afs_put_cb_interest(afs_i2net(inode), cbi);
rcu_assign_pointer(vnode->cb_interest, NULL);
}
write_sequnlock(&vnode->cb_lock);
while (!list_empty(&vnode->wb_keys)) {
struct afs_wb_key *wbk = list_entry(vnode->wb_keys.next,
struct afs_wb_key, vnode_link);
@ -808,16 +783,24 @@ void afs_evict_inode(struct inode *inode)
_leave("");
}
static void afs_setattr_success(struct afs_operation *op)
{
afs_vnode_commit_status(op, &op->file[0]);
}
static const struct afs_operation_ops afs_setattr_operation = {
.issue_afs_rpc = afs_fs_setattr,
.issue_yfs_rpc = yfs_fs_setattr,
.success = afs_setattr_success,
};
/*
* set the attributes of an inode
*/
int afs_setattr(struct dentry *dentry, struct iattr *attr)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_operation *op;
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
struct key *key;
int ret = -ENOMEM;
_enter("{%llx:%llu},{n=%pd},%x",
vnode->fid.vid, vnode->fid.vnode, dentry,
@ -829,48 +812,22 @@ int afs_setattr(struct dentry *dentry, struct iattr *attr)
return 0;
}
scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
if (!scb)
goto error;
/* flush any dirty data outstanding on a regular file */
if (S_ISREG(vnode->vfs_inode.i_mode))
filemap_write_and_wait(vnode->vfs_inode.i_mapping);
if (attr->ia_valid & ATTR_FILE) {
key = afs_file_key(attr->ia_file);
} else {
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error_scb;
}
}
op = afs_alloc_operation(((attr->ia_valid & ATTR_FILE) ?
afs_file_key(attr->ia_file) : NULL),
vnode->volume);
if (IS_ERR(op))
return PTR_ERR(op);
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, false)) {
afs_dataversion_t data_version = vnode->status.data_version;
afs_op_set_vnode(op, 0, vnode);
op->setattr.attr = attr;
if (attr->ia_valid & ATTR_SIZE)
data_version++;
if (attr->ia_valid & ATTR_SIZE)
op->file[0].dv_delta = 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_setattr(&fc, attr, scb);
}
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
if (!(attr->ia_valid & ATTR_FILE))
key_put(key);
error_scb:
kfree(scb);
error:
_leave(" = %d", ret);
return ret;
op->ops = &afs_setattr_operation;
return afs_do_sync_operation(op);
}

Просмотреть файл

@ -59,13 +59,6 @@ struct afs_fs_context {
struct key *key; /* key to use for secure mounting */
};
struct afs_iget_data {
struct afs_fid fid;
struct afs_volume *volume; /* volume on which resides */
unsigned int cb_v_break; /* Pre-fetch volume break count */
unsigned int cb_s_break; /* Pre-fetch server break count */
};
enum afs_call_state {
AFS_CALL_CL_REQUESTING, /* Client: Request is being sent */
AFS_CALL_CL_AWAIT_REPLY, /* Client: Awaiting reply */
@ -90,7 +83,6 @@ struct afs_addr_list {
unsigned char nr_ipv4; /* Number of IPv4 addresses */
enum dns_record_source source:8;
enum dns_lookup_status status:8;
unsigned long probed; /* Mask of servers that have been probed */
unsigned long failed; /* Mask of addrs that failed locally/ICMP */
unsigned long responded; /* Mask of addrs that responded */
struct sockaddr_rxrpc addrs[];
@ -111,10 +103,7 @@ struct afs_call {
struct afs_net *net; /* The network namespace */
struct afs_server *server; /* The fileserver record if fs op (pins ref) */
struct afs_vlserver *vlserver; /* The vlserver record if vl op */
struct afs_cb_interest *cbi; /* Callback interest for server used */
struct afs_vnode *lvnode; /* vnode being locked */
void *request; /* request data (first part) */
struct address_space *mapping; /* Pages being written from */
struct iov_iter def_iter; /* Default buffer/data iterator */
struct iov_iter *iter; /* Iterator currently in use */
union { /* Convenience for ->def_iter */
@ -126,32 +115,19 @@ struct afs_call {
long ret0; /* Value to reply with instead of 0 */
struct afs_addr_list *ret_alist;
struct afs_vldb_entry *ret_vldb;
struct afs_acl *ret_acl;
char *ret_str;
};
struct afs_fid *out_fid;
struct afs_status_cb *out_dir_scb;
struct afs_status_cb *out_scb;
struct yfs_acl *out_yacl;
struct afs_volsync *out_volsync;
struct afs_volume_status *out_volstatus;
struct afs_read *read_request;
struct afs_operation *op;
unsigned int server_index;
pgoff_t first; /* first page in mapping to deal with */
pgoff_t last; /* last page in mapping to deal with */
atomic_t usage;
enum afs_call_state state;
spinlock_t state_lock;
int error; /* error code */
u32 abort_code; /* Remote abort ID or 0 */
u32 epoch;
unsigned int max_lifespan; /* Maximum lifespan to set if not 0 */
unsigned request_size; /* size of request data */
unsigned reply_max; /* maximum size of reply */
unsigned first_offset; /* offset into mapping[first] */
union {
unsigned last_to; /* amount of mapping[last] */
unsigned count2; /* count used in unmarshalling */
};
unsigned count2; /* count used in unmarshalling */
unsigned char unmarshall; /* unmarshalling phase */
unsigned char addr_ix; /* Address in ->alist */
bool drop_ref; /* T if need to drop ref for incoming call */
@ -161,6 +137,7 @@ struct afs_call {
bool upgrade; /* T to request service upgrade */
bool have_reply_time; /* T if have got reply_time */
bool intr; /* T if interruptible */
bool unmarshalling_error; /* T if an unmarshalling error occurred */
u16 service_id; /* Actual service ID (after upgrade) */
unsigned int debug_id; /* Trace ID */
u32 operation_ID; /* operation ID for an incoming call */
@ -291,6 +268,7 @@ struct afs_net {
struct timer_list cells_timer;
atomic_t cells_outstanding;
seqlock_t cells_lock;
struct mutex cells_alias_lock;
struct mutex proc_cells_lock;
struct hlist_head proc_cells;
@ -299,9 +277,10 @@ struct afs_net {
* cell, but in practice, people create aliases and subsets and there's
* no easy way to distinguish them.
*/
seqlock_t fs_lock; /* For fs_servers */
seqlock_t fs_lock; /* For fs_servers, fs_probe_*, fs_proc */
struct rb_root fs_servers; /* afs_server (by server UUID or address) */
struct list_head fs_updates; /* afs_server (by update_at) */
struct list_head fs_probe_fast; /* List of afs_server to probe at 30s intervals */
struct list_head fs_probe_slow; /* List of afs_server to probe at 5m intervals */
struct hlist_head fs_proc; /* procfs servers list */
struct hlist_head fs_addresses4; /* afs_server (by lowest IPv4 addr) */
@ -310,6 +289,9 @@ struct afs_net {
struct work_struct fs_manager;
struct timer_list fs_timer;
struct work_struct fs_prober;
struct timer_list fs_probe_timer;
atomic_t servers_outstanding;
/* File locking renewal management */
@ -360,8 +342,10 @@ enum afs_cell_state {
* for authentication and encryption. The cell name is not typically used in
* the protocol.
*
* There is no easy way to determine if two cells are aliases or one is a
* subset of another.
* Two cells are determined to be aliases if they have an explicit alias (YFS
* only), share any VL servers in common or have at least one volume in common.
* "In common" means that the address list of the VL servers or the fileservers
* share at least one endpoint.
*/
struct afs_cell {
union {
@ -369,6 +353,8 @@ struct afs_cell {
struct rb_node net_node; /* Node in net->cells */
};
struct afs_net *net;
struct afs_cell *alias_of; /* The cell this is an alias of */
struct afs_volume *root_volume; /* The root.cell volume if there is one */
struct key *anonymous_key; /* anonymous user key for this cell */
struct work_struct manager; /* Manager for init/deinit/dns */
struct hlist_node proc_link; /* /proc cell list link */
@ -381,15 +367,21 @@ struct afs_cell {
unsigned long flags;
#define AFS_CELL_FL_NO_GC 0 /* The cell was added manually, don't auto-gc */
#define AFS_CELL_FL_DO_LOOKUP 1 /* DNS lookup requested */
#define AFS_CELL_FL_CHECK_ALIAS 2 /* Need to check for aliases */
enum afs_cell_state state;
short error;
enum dns_record_source dns_source:8; /* Latest source of data from lookup */
enum dns_lookup_status dns_status:8; /* Latest status of data from lookup */
unsigned int dns_lookup_count; /* Counter of DNS lookups */
/* The volumes belonging to this cell */
struct rb_root volumes; /* Tree of volumes on this server */
struct hlist_head proc_volumes; /* procfs volume list */
seqlock_t volume_lock; /* For volumes */
/* Active fileserver interaction state. */
struct list_head proc_volumes; /* procfs volume list */
rwlock_t proc_lock;
struct rb_root fs_servers; /* afs_server (by server UUID) */
seqlock_t fs_lock; /* For fs_servers */
/* VL server list. */
rwlock_t vl_servers_lock; /* Lock on vl_servers */
@ -471,6 +463,7 @@ struct afs_vldb_entry {
#define AFS_VLDB_QUERY_ERROR 4 /* - VL server returned error */
uuid_t fs_server[AFS_NMAXNSERVERS];
u32 addr_version[AFS_NMAXNSERVERS]; /* Registration change counters */
u8 fs_mask[AFS_NMAXNSERVERS];
#define AFS_VOL_VTM_RW 0x01 /* R/W version of the volume is available (on this server) */
#define AFS_VOL_VTM_RO 0x02 /* R/O version of the volume is available (on this server) */
@ -492,94 +485,64 @@ struct afs_server {
};
struct afs_addr_list __rcu *addresses;
struct rb_node uuid_rb; /* Link in net->servers */
struct afs_cell *cell; /* Cell to which belongs (pins ref) */
struct rb_node uuid_rb; /* Link in net->fs_servers */
struct afs_server __rcu *uuid_next; /* Next server with same UUID */
struct afs_server *uuid_prev; /* Previous server with same UUID */
struct list_head probe_link; /* Link in net->fs_probe_list */
struct hlist_node addr4_link; /* Link in net->fs_addresses4 */
struct hlist_node addr6_link; /* Link in net->fs_addresses6 */
struct hlist_node proc_link; /* Link in net->fs_proc */
struct afs_server *gc_next; /* Next server in manager's list */
time64_t put_time; /* Time at which last put */
time64_t update_at; /* Time at which to next update the record */
time64_t unuse_time; /* Time at which last unused */
unsigned long flags;
#define AFS_SERVER_FL_NOT_READY 1 /* The record is not ready for use */
#define AFS_SERVER_FL_NOT_FOUND 2 /* VL server says no such server */
#define AFS_SERVER_FL_VL_FAIL 3 /* Failed to access VL server */
#define AFS_SERVER_FL_UPDATING 4
#define AFS_SERVER_FL_PROBED 5 /* The fileserver has been probed */
#define AFS_SERVER_FL_PROBING 6 /* Fileserver is being probed */
#define AFS_SERVER_FL_NO_IBULK 7 /* Fileserver doesn't support FS.InlineBulkStatus */
#define AFS_SERVER_FL_RESPONDING 0 /* The server is responding */
#define AFS_SERVER_FL_UPDATING 1
#define AFS_SERVER_FL_NEEDS_UPDATE 2 /* Fileserver address list is out of date */
#define AFS_SERVER_FL_NOT_READY 4 /* The record is not ready for use */
#define AFS_SERVER_FL_NOT_FOUND 5 /* VL server says no such server */
#define AFS_SERVER_FL_VL_FAIL 6 /* Failed to access VL server */
#define AFS_SERVER_FL_MAY_HAVE_CB 8 /* May have callbacks on this fileserver */
#define AFS_SERVER_FL_IS_YFS 9 /* Server is YFS not AFS */
#define AFS_SERVER_FL_NO_RM2 10 /* Fileserver doesn't support YFS.RemoveFile2 */
#define AFS_SERVER_FL_HAVE_EPOCH 11 /* ->epoch is valid */
atomic_t usage;
#define AFS_SERVER_FL_IS_YFS 16 /* Server is YFS not AFS */
#define AFS_SERVER_FL_NO_IBULK 17 /* Fileserver doesn't support FS.InlineBulkStatus */
#define AFS_SERVER_FL_NO_RM2 18 /* Fileserver doesn't support YFS.RemoveFile2 */
atomic_t ref; /* Object refcount */
atomic_t active; /* Active user count */
u32 addr_version; /* Address list version */
u32 cm_epoch; /* Server RxRPC epoch */
unsigned int rtt; /* Server's current RTT in uS */
unsigned int debug_id; /* Debugging ID for traces */
/* file service access */
rwlock_t fs_lock; /* access lock */
/* callback promise management */
struct hlist_head cb_volumes; /* List of volume interests on this server */
unsigned cb_s_break; /* Break-everything counter. */
rwlock_t cb_break_lock; /* Volume finding lock */
/* Probe state */
unsigned long probed_at; /* Time last probe was dispatched (jiffies) */
wait_queue_head_t probe_wq;
atomic_t probe_outstanding;
spinlock_t probe_lock;
struct {
unsigned int rtt; /* RTT as ktime/64 */
unsigned int rtt; /* RTT in uS */
u32 abort_code;
u32 cm_epoch;
short error;
bool responded:1;
bool is_yfs:1;
bool not_yfs:1;
bool local_failure:1;
bool cm_probed:1;
bool said_rebooted:1;
bool said_inconsistent:1;
} probe;
};
/*
* Volume collation in the server's callback interest list.
*/
struct afs_vol_interest {
struct hlist_node srv_link; /* Link in server->cb_volumes */
struct hlist_head cb_interests; /* List of callback interests on the server */
union {
struct rcu_head rcu;
afs_volid_t vid; /* Volume ID to match */
};
unsigned int usage;
};
/*
* Interest by a superblock on a server.
*/
struct afs_cb_interest {
struct hlist_node cb_vlink; /* Link in vol_interest->cb_interests */
struct afs_vol_interest *vol_interest;
struct afs_server *server; /* Server on which this interest resides */
struct super_block *sb; /* Superblock on which inodes reside */
union {
struct rcu_head rcu;
afs_volid_t vid; /* Volume ID to match */
};
refcount_t usage;
};
/*
* Replaceable server list.
* Replaceable volume server list.
*/
struct afs_server_entry {
struct afs_server *server;
struct afs_cb_interest *cb_interest;
};
struct afs_server_list {
afs_volid_t vids[AFS_MAXTYPES]; /* Volume IDs */
refcount_t usage;
unsigned char nr_servers;
unsigned char preferred; /* Preferred server */
@ -593,11 +556,16 @@ struct afs_server_list {
* Live AFS volume management.
*/
struct afs_volume {
afs_volid_t vid; /* volume ID */
union {
struct rcu_head rcu;
afs_volid_t vid; /* volume ID */
};
atomic_t usage;
time64_t update_at; /* Time at which to next update */
struct afs_cell *cell; /* Cell to which belongs (pins ref) */
struct list_head proc_link; /* Link in cell->vl_proc */
struct rb_node cell_node; /* Link in cell->volumes */
struct hlist_node proc_link; /* Link in cell->proc_volumes */
struct super_block __rcu *sb; /* Superblock on which inodes reside */
unsigned long flags;
#define AFS_VOLUME_NEEDS_UPDATE 0 /* - T if an update needs performing */
#define AFS_VOLUME_UPDATING 1 /* - T if an update is in progress */
@ -605,10 +573,11 @@ struct afs_volume {
#define AFS_VOLUME_DELETED 3 /* - T if volume appears deleted */
#define AFS_VOLUME_OFFLINE 4 /* - T if volume offline notice given */
#define AFS_VOLUME_BUSY 5 /* - T if volume busy notice given */
#define AFS_VOLUME_MAYBE_NO_IBULK 6 /* - T if some servers don't have InlineBulkStatus */
#ifdef CONFIG_AFS_FSCACHE
struct fscache_cookie *cache; /* caching cookie */
#endif
struct afs_server_list *servers; /* List of servers on which volume resides */
struct afs_server_list __rcu *servers; /* List of servers on which volume resides */
rwlock_t servers_lock; /* Lock for ->servers */
unsigned int servers_seq; /* Incremented each time ->servers changes */
@ -616,7 +585,6 @@ struct afs_volume {
rwlock_t cb_v_break_lock;
afs_voltype_t type; /* type of volume */
short error;
char type_force; /* force volume type (suppress R/O -> R/W) */
u8 name_len;
u8 name[AFS_MAXVOLNAME + 1]; /* NUL-padded volume name */
@ -677,11 +645,11 @@ struct afs_vnode {
afs_lock_type_t lock_type : 8;
/* outstanding callback notification on this file */
struct afs_cb_interest __rcu *cb_interest; /* Server on which this resides */
void *cb_server; /* Server with callback/filelock */
unsigned int cb_s_break; /* Mass break counter on ->server */
unsigned int cb_v_break; /* Mass break counter on ->volume */
unsigned int cb_break; /* Break counter on vnode */
seqlock_t cb_lock; /* Lock for ->cb_interest, ->status, ->cb_*break */
seqlock_t cb_lock; /* Lock for ->cb_server, ->status, ->cb_*break */
time64_t cb_expires_at; /* time at which callback expires */
};
@ -758,29 +726,118 @@ struct afs_vl_cursor {
};
/*
* Cursor for iterating over a set of fileservers.
* Fileserver operation methods.
*/
struct afs_fs_cursor {
const struct afs_call_type *type; /* Type of call done */
struct afs_addr_cursor ac;
struct afs_operation_ops {
void (*issue_afs_rpc)(struct afs_operation *op);
void (*issue_yfs_rpc)(struct afs_operation *op);
void (*success)(struct afs_operation *op);
void (*aborted)(struct afs_operation *op);
void (*edit_dir)(struct afs_operation *op);
void (*put)(struct afs_operation *op);
};
struct afs_vnode_param {
struct afs_vnode *vnode;
struct afs_server_list *server_list; /* Current server list (pins ref) */
struct afs_cb_interest *cbi; /* Server on which this resides (pins ref) */
struct key *key; /* Key for the server */
unsigned long untried; /* Bitmask of untried servers */
unsigned int cb_break; /* cb_break + cb_s_break before the call */
unsigned int cb_break_2; /* cb_break + cb_s_break (2nd vnode) */
short index; /* Current server */
struct afs_fid fid; /* Fid to access */
struct afs_status_cb scb; /* Returned status and callback promise */
afs_dataversion_t dv_before; /* Data version before the call */
unsigned int cb_break_before; /* cb_break + cb_s_break before the call */
u8 dv_delta; /* Expected change in data version */
bool put_vnode; /* T if we have a ref on the vnode */
bool need_io_lock; /* T if we need the I/O lock on this */
};
/*
* Fileserver operation wrapper, handling server and address rotation
* asynchronously. May make simultaneous calls to multiple servers.
*/
struct afs_operation {
struct afs_net *net; /* Network namespace */
struct key *key; /* Key for the cell */
const struct afs_call_type *type; /* Type of call done */
const struct afs_operation_ops *ops;
/* Parameters/results for the operation */
struct afs_volume *volume; /* Volume being accessed */
struct afs_vnode_param file[2];
struct afs_vnode_param *more_files;
struct afs_volsync volsync;
struct dentry *dentry; /* Dentry to be altered */
struct dentry *dentry_2; /* Second dentry to be altered */
struct timespec64 mtime; /* Modification time to record */
short nr_files; /* Number of entries in file[], more_files */
short error;
unsigned short flags;
#define AFS_FS_CURSOR_STOP 0x0001 /* Set to cease iteration */
#define AFS_FS_CURSOR_VBUSY 0x0002 /* Set if seen VBUSY */
#define AFS_FS_CURSOR_VMOVED 0x0004 /* Set if seen VMOVED */
#define AFS_FS_CURSOR_VNOVOL 0x0008 /* Set if seen VNOVOL */
#define AFS_FS_CURSOR_CUR_ONLY 0x0010 /* Set if current server only (file lock held) */
#define AFS_FS_CURSOR_NO_VSLEEP 0x0020 /* Set to prevent sleep on VBUSY, VOFFLINE, ... */
#define AFS_FS_CURSOR_INTR 0x0040 /* Set if op is interruptible */
unsigned int abort_code;
unsigned int debug_id;
unsigned int cb_v_break; /* Volume break counter before op */
unsigned int cb_s_break; /* Server break counter before op */
union {
struct {
int which; /* Which ->file[] to fetch for */
} fetch_status;
struct {
int reason; /* enum afs_edit_dir_reason */
mode_t mode;
const char *symlink;
} create;
struct {
bool need_rehash;
} unlink;
struct {
struct dentry *rehash;
struct dentry *tmp;
bool new_negative;
} rename;
struct {
struct afs_read *req;
} fetch;
struct {
struct afs_vnode *lvnode; /* vnode being locked */
afs_lock_type_t type;
} lock;
struct {
struct address_space *mapping; /* Pages being written from */
pgoff_t first; /* first page in mapping to deal with */
pgoff_t last; /* last page in mapping to deal with */
unsigned first_offset; /* offset into mapping[first] */
unsigned last_to; /* amount of mapping[last] */
} store;
struct {
struct iattr *attr;
} setattr;
struct afs_acl *acl;
struct yfs_acl *yacl;
struct {
struct afs_volume_status vs;
struct kstatfs *buf;
} volstatus;
};
/* Fileserver iteration state */
struct afs_addr_cursor ac;
struct afs_server_list *server_list; /* Current server list (pins ref) */
struct afs_server *server; /* Server we're using (ref pinned by server_list) */
struct afs_call *call;
unsigned long untried; /* Bitmask of untried servers */
short index; /* Current server */
unsigned short nr_iterations; /* Number of server iterations */
unsigned int flags;
#define AFS_OPERATION_STOP 0x0001 /* Set to cease iteration */
#define AFS_OPERATION_VBUSY 0x0002 /* Set if seen VBUSY */
#define AFS_OPERATION_VMOVED 0x0004 /* Set if seen VMOVED */
#define AFS_OPERATION_VNOVOL 0x0008 /* Set if seen VNOVOL */
#define AFS_OPERATION_CUR_ONLY 0x0010 /* Set if current server only (file lock held) */
#define AFS_OPERATION_NO_VSLEEP 0x0020 /* Set to prevent sleep on VBUSY, VOFFLINE, ... */
#define AFS_OPERATION_UNINTR 0x0040 /* Set if op is uninterruptible */
#define AFS_OPERATION_DOWNGRADE 0x0080 /* Set to retry with downgraded opcode */
#define AFS_OPERATION_LOCK_0 0x0100 /* Set if have io_lock on file[0] */
#define AFS_OPERATION_LOCK_1 0x0200 /* Set if have io_lock on file[1] */
#define AFS_OPERATION_TRIED_ALL 0x0400 /* Set if we've tried all the fileservers */
#define AFS_OPERATION_RETRY_SERVER 0x0800 /* Set if we should retry the current server */
};
/*
@ -838,29 +895,15 @@ extern void __afs_break_callback(struct afs_vnode *, enum afs_cb_break_reason);
extern void afs_break_callback(struct afs_vnode *, enum afs_cb_break_reason);
extern void afs_break_callbacks(struct afs_server *, size_t, struct afs_callback_break *);
extern int afs_register_server_cb_interest(struct afs_vnode *,
struct afs_server_list *, unsigned int);
extern void afs_put_cb_interest(struct afs_net *, struct afs_cb_interest *);
extern void afs_clear_callback_interests(struct afs_net *, struct afs_server_list *);
static inline struct afs_cb_interest *afs_get_cb_interest(struct afs_cb_interest *cbi)
{
if (cbi)
refcount_inc(&cbi->usage);
return cbi;
}
static inline unsigned int afs_calc_vnode_cb_break(struct afs_vnode *vnode)
{
return vnode->cb_break + vnode->cb_v_break;
}
static inline bool afs_cb_is_broken(unsigned int cb_break,
const struct afs_vnode *vnode,
const struct afs_cb_interest *cbi)
const struct afs_vnode *vnode)
{
return !cbi || cb_break != (vnode->cb_break +
vnode->volume->cb_v_break);
return cb_break != (vnode->cb_break + vnode->volume->cb_v_break);
}
/*
@ -952,71 +995,81 @@ extern int afs_flock(struct file *, int, struct file_lock *);
/*
* fsclient.c
*/
extern int afs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_status_cb *,
struct afs_volsync *);
extern int afs_fs_give_up_callbacks(struct afs_net *, struct afs_server *);
extern int afs_fs_fetch_data(struct afs_fs_cursor *, struct afs_status_cb *, struct afs_read *);
extern int afs_fs_create(struct afs_fs_cursor *, const char *, umode_t,
struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *);
extern int afs_fs_remove(struct afs_fs_cursor *, struct afs_vnode *, const char *, bool,
struct afs_status_cb *);
extern int afs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *,
struct afs_status_cb *, struct afs_status_cb *);
extern int afs_fs_symlink(struct afs_fs_cursor *, const char *, const char *,
struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *);
extern int afs_fs_rename(struct afs_fs_cursor *, const char *,
struct afs_vnode *, const char *,
struct afs_status_cb *, struct afs_status_cb *);
extern int afs_fs_store_data(struct afs_fs_cursor *, struct address_space *,
pgoff_t, pgoff_t, unsigned, unsigned, struct afs_status_cb *);
extern int afs_fs_setattr(struct afs_fs_cursor *, struct iattr *, struct afs_status_cb *);
extern int afs_fs_get_volume_status(struct afs_fs_cursor *, struct afs_volume_status *);
extern int afs_fs_set_lock(struct afs_fs_cursor *, afs_lock_type_t, struct afs_status_cb *);
extern int afs_fs_extend_lock(struct afs_fs_cursor *, struct afs_status_cb *);
extern int afs_fs_release_lock(struct afs_fs_cursor *, struct afs_status_cb *);
extern void afs_fs_fetch_status(struct afs_operation *);
extern void afs_fs_fetch_data(struct afs_operation *);
extern void afs_fs_create_file(struct afs_operation *);
extern void afs_fs_make_dir(struct afs_operation *);
extern void afs_fs_remove_file(struct afs_operation *);
extern void afs_fs_remove_dir(struct afs_operation *);
extern void afs_fs_link(struct afs_operation *);
extern void afs_fs_symlink(struct afs_operation *);
extern void afs_fs_rename(struct afs_operation *);
extern void afs_fs_store_data(struct afs_operation *);
extern void afs_fs_setattr(struct afs_operation *);
extern void afs_fs_get_volume_status(struct afs_operation *);
extern void afs_fs_set_lock(struct afs_operation *);
extern void afs_fs_extend_lock(struct afs_operation *);
extern void afs_fs_release_lock(struct afs_operation *);
extern int afs_fs_give_up_all_callbacks(struct afs_net *, struct afs_server *,
struct afs_addr_cursor *, struct key *);
extern struct afs_call *afs_fs_get_capabilities(struct afs_net *, struct afs_server *,
struct afs_addr_cursor *, struct key *,
unsigned int);
extern int afs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *,
struct afs_fid *, struct afs_status_cb *,
unsigned int, struct afs_volsync *);
extern int afs_fs_fetch_status(struct afs_fs_cursor *, struct afs_net *,
struct afs_fid *, struct afs_status_cb *,
struct afs_volsync *);
extern bool afs_fs_get_capabilities(struct afs_net *, struct afs_server *,
struct afs_addr_cursor *, struct key *);
extern void afs_fs_inline_bulk_status(struct afs_operation *);
struct afs_acl {
u32 size;
u8 data[];
};
extern struct afs_acl *afs_fs_fetch_acl(struct afs_fs_cursor *, struct afs_status_cb *);
extern int afs_fs_store_acl(struct afs_fs_cursor *, const struct afs_acl *,
struct afs_status_cb *);
extern void afs_fs_fetch_acl(struct afs_operation *);
extern void afs_fs_store_acl(struct afs_operation *);
/*
* fs_operation.c
*/
extern struct afs_operation *afs_alloc_operation(struct key *, struct afs_volume *);
extern int afs_put_operation(struct afs_operation *);
extern bool afs_begin_vnode_operation(struct afs_operation *);
extern void afs_wait_for_operation(struct afs_operation *);
extern int afs_do_sync_operation(struct afs_operation *);
static inline void afs_op_nomem(struct afs_operation *op)
{
op->error = -ENOMEM;
}
static inline void afs_op_set_vnode(struct afs_operation *op, unsigned int n,
struct afs_vnode *vnode)
{
op->file[n].vnode = vnode;
op->file[n].need_io_lock = true;
}
static inline void afs_op_set_fid(struct afs_operation *op, unsigned int n,
const struct afs_fid *fid)
{
op->file[n].fid = *fid;
}
/*
* fs_probe.c
*/
extern void afs_fileserver_probe_result(struct afs_call *);
extern int afs_probe_fileservers(struct afs_net *, struct key *, struct afs_server_list *);
extern void afs_fs_probe_fileserver(struct afs_net *, struct afs_server *, struct key *, bool);
extern int afs_wait_for_fs_probes(struct afs_server_list *, unsigned long);
extern void afs_probe_fileserver(struct afs_net *, struct afs_server *);
extern void afs_fs_probe_dispatcher(struct work_struct *);
extern int afs_wait_for_one_fs_probe(struct afs_server *, bool);
/*
* inode.c
*/
extern void afs_vnode_commit_status(struct afs_fs_cursor *,
struct afs_vnode *,
unsigned int,
const afs_dataversion_t *,
struct afs_status_cb *);
extern void afs_vnode_commit_status(struct afs_operation *, struct afs_vnode_param *);
extern int afs_fetch_status(struct afs_vnode *, struct key *, bool, afs_access_t *);
extern int afs_iget5_test(struct inode *, void *);
extern int afs_ilookup5_test_by_fid(struct inode *, void *);
extern struct inode *afs_iget_pseudo_dir(struct super_block *, bool);
extern struct inode *afs_iget(struct super_block *, struct key *,
struct afs_iget_data *, struct afs_status_cb *,
struct afs_cb_interest *,
struct afs_vnode *);
extern struct inode *afs_iget(struct afs_operation *, struct afs_vnode_param *);
extern struct inode *afs_root_iget(struct super_block *, struct key *);
extern void afs_zap_data(struct afs_vnode *);
extern bool afs_check_validity(struct afs_vnode *);
extern int afs_validate(struct afs_vnode *, struct key *);
@ -1104,11 +1157,8 @@ static inline void afs_put_sysnames(struct afs_sysnames *sysnames) {}
/*
* rotate.c
*/
extern bool afs_begin_vnode_operation(struct afs_fs_cursor *, struct afs_vnode *,
struct key *, bool);
extern bool afs_select_fileserver(struct afs_fs_cursor *);
extern bool afs_select_current_fileserver(struct afs_fs_cursor *);
extern int afs_end_vnode_operation(struct afs_fs_cursor *);
extern bool afs_select_fileserver(struct afs_operation *);
extern void afs_dump_edestaddrreq(const struct afs_operation *);
/*
* rxrpc.c
@ -1128,12 +1178,17 @@ extern void afs_flat_call_destructor(struct afs_call *);
extern void afs_send_empty_reply(struct afs_call *);
extern void afs_send_simple_reply(struct afs_call *, const void *, size_t);
extern int afs_extract_data(struct afs_call *, bool);
extern int afs_protocol_error(struct afs_call *, int, enum afs_eproto_cause);
extern int afs_protocol_error(struct afs_call *, enum afs_eproto_cause);
static inline void afs_set_fc_call(struct afs_call *call, struct afs_fs_cursor *fc)
static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *call,
gfp_t gfp)
{
call->intr = fc->flags & AFS_FS_CURSOR_INTR;
fc->type = call->type;
op->call = call;
op->type = call->type;
call->op = op;
call->key = op->key;
call->intr = !(op->flags & AFS_OPERATION_UNINTR);
afs_make_call(&op->ac, call, gfp);
}
static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size)
@ -1241,13 +1296,33 @@ extern spinlock_t afs_server_peer_lock;
extern struct afs_server *afs_find_server(struct afs_net *,
const struct sockaddr_rxrpc *);
extern struct afs_server *afs_find_server_by_uuid(struct afs_net *, const uuid_t *);
extern struct afs_server *afs_lookup_server(struct afs_cell *, struct key *, const uuid_t *);
extern struct afs_server *afs_lookup_server(struct afs_cell *, struct key *, const uuid_t *, u32);
extern struct afs_server *afs_get_server(struct afs_server *, enum afs_server_trace);
extern struct afs_server *afs_use_server(struct afs_server *, enum afs_server_trace);
extern void afs_unuse_server(struct afs_net *, struct afs_server *, enum afs_server_trace);
extern void afs_unuse_server_notime(struct afs_net *, struct afs_server *, enum afs_server_trace);
extern void afs_put_server(struct afs_net *, struct afs_server *, enum afs_server_trace);
extern void afs_manage_servers(struct work_struct *);
extern void afs_servers_timer(struct timer_list *);
extern void afs_fs_probe_timer(struct timer_list *);
extern void __net_exit afs_purge_servers(struct afs_net *);
extern bool afs_check_server_record(struct afs_fs_cursor *, struct afs_server *);
extern bool afs_check_server_record(struct afs_operation *, struct afs_server *);
static inline void afs_inc_servers_outstanding(struct afs_net *net)
{
atomic_inc(&net->servers_outstanding);
}
static inline void afs_dec_servers_outstanding(struct afs_net *net)
{
if (atomic_dec_and_test(&net->servers_outstanding))
wake_up_var(&net->servers_outstanding);
}
static inline bool afs_is_probing_server(struct afs_server *server)
{
return list_empty(&server->probe_link);
}
/*
* server_list.c
@ -1279,6 +1354,12 @@ extern struct afs_addr_list *afs_vl_get_addrs_u(struct afs_vl_cursor *, const uu
extern struct afs_call *afs_vl_get_capabilities(struct afs_net *, struct afs_addr_cursor *,
struct key *, struct afs_vlserver *, unsigned int);
extern struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *, const uuid_t *);
extern char *afs_yfsvl_get_cell_name(struct afs_vl_cursor *);
/*
* vl_alias.c
*/
extern int afs_cell_detect_alias(struct afs_cell *, struct key *);
/*
* vl_probe.c
@ -1322,18 +1403,12 @@ extern struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *,
/*
* volume.c
*/
static inline struct afs_volume *__afs_get_volume(struct afs_volume *volume)
{
if (volume)
atomic_inc(&volume->usage);
return volume;
}
extern struct afs_volume *afs_create_volume(struct afs_fs_context *);
extern void afs_activate_volume(struct afs_volume *);
extern void afs_deactivate_volume(struct afs_volume *);
extern void afs_put_volume(struct afs_cell *, struct afs_volume *);
extern int afs_check_volume_status(struct afs_volume *, struct afs_fs_cursor *);
extern struct afs_volume *afs_get_volume(struct afs_volume *, enum afs_volume_trace);
extern void afs_put_volume(struct afs_net *, struct afs_volume *, enum afs_volume_trace);
extern int afs_check_volume_status(struct afs_volume *, struct afs_operation *);
/*
* write.c
@ -1362,36 +1437,24 @@ extern ssize_t afs_listxattr(struct dentry *, char *, size_t);
/*
* yfsclient.c
*/
extern int yfs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_status_cb *,
struct afs_volsync *);
extern int yfs_fs_fetch_data(struct afs_fs_cursor *, struct afs_status_cb *, struct afs_read *);
extern int yfs_fs_create_file(struct afs_fs_cursor *, const char *, umode_t, struct afs_status_cb *,
struct afs_fid *, struct afs_status_cb *);
extern int yfs_fs_make_dir(struct afs_fs_cursor *, const char *, umode_t, struct afs_status_cb *,
struct afs_fid *, struct afs_status_cb *);
extern int yfs_fs_remove_file2(struct afs_fs_cursor *, struct afs_vnode *, const char *,
struct afs_status_cb *, struct afs_status_cb *);
extern int yfs_fs_remove(struct afs_fs_cursor *, struct afs_vnode *, const char *, bool,
struct afs_status_cb *);
extern int yfs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *,
struct afs_status_cb *, struct afs_status_cb *);
extern int yfs_fs_symlink(struct afs_fs_cursor *, const char *, const char *,
struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *);
extern int yfs_fs_rename(struct afs_fs_cursor *, const char *, struct afs_vnode *, const char *,
struct afs_status_cb *, struct afs_status_cb *);
extern int yfs_fs_store_data(struct afs_fs_cursor *, struct address_space *,
pgoff_t, pgoff_t, unsigned, unsigned, struct afs_status_cb *);
extern int yfs_fs_setattr(struct afs_fs_cursor *, struct iattr *, struct afs_status_cb *);
extern int yfs_fs_get_volume_status(struct afs_fs_cursor *, struct afs_volume_status *);
extern int yfs_fs_set_lock(struct afs_fs_cursor *, afs_lock_type_t, struct afs_status_cb *);
extern int yfs_fs_extend_lock(struct afs_fs_cursor *, struct afs_status_cb *);
extern int yfs_fs_release_lock(struct afs_fs_cursor *, struct afs_status_cb *);
extern int yfs_fs_fetch_status(struct afs_fs_cursor *, struct afs_net *,
struct afs_fid *, struct afs_status_cb *,
struct afs_volsync *);
extern int yfs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *,
struct afs_fid *, struct afs_status_cb *,
unsigned int, struct afs_volsync *);
extern void yfs_fs_fetch_file_status(struct afs_operation *);
extern void yfs_fs_fetch_data(struct afs_operation *);
extern void yfs_fs_create_file(struct afs_operation *);
extern void yfs_fs_make_dir(struct afs_operation *);
extern void yfs_fs_remove_file2(struct afs_operation *);
extern void yfs_fs_remove_file(struct afs_operation *);
extern void yfs_fs_remove_dir(struct afs_operation *);
extern void yfs_fs_link(struct afs_operation *);
extern void yfs_fs_symlink(struct afs_operation *);
extern void yfs_fs_rename(struct afs_operation *);
extern void yfs_fs_store_data(struct afs_operation *);
extern void yfs_fs_setattr(struct afs_operation *);
extern void yfs_fs_get_volume_status(struct afs_operation *);
extern void yfs_fs_set_lock(struct afs_operation *);
extern void yfs_fs_extend_lock(struct afs_operation *);
extern void yfs_fs_release_lock(struct afs_operation *);
extern void yfs_fs_fetch_status(struct afs_operation *);
extern void yfs_fs_inline_bulk_status(struct afs_operation *);
struct yfs_acl {
struct afs_acl *acl; /* Dir/file/symlink ACL */
@ -1404,10 +1467,8 @@ struct yfs_acl {
};
extern void yfs_free_opaque_acl(struct yfs_acl *);
extern struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_fs_cursor *, struct yfs_acl *,
struct afs_status_cb *);
extern int yfs_fs_store_opaque_acl2(struct afs_fs_cursor *, const struct afs_acl *,
struct afs_status_cb *);
extern void yfs_fs_fetch_opaque_acl(struct afs_operation *);
extern void yfs_fs_store_opaque_acl2(struct afs_operation *);
/*
* Miscellaneous inline functions.
@ -1422,15 +1483,29 @@ static inline struct inode *AFS_VNODE_TO_I(struct afs_vnode *vnode)
return &vnode->vfs_inode;
}
static inline void afs_check_for_remote_deletion(struct afs_fs_cursor *fc,
static inline void afs_check_for_remote_deletion(struct afs_operation *op,
struct afs_vnode *vnode)
{
if (fc->ac.error == -ENOENT) {
if (op->error == -ENOENT) {
set_bit(AFS_VNODE_DELETED, &vnode->flags);
afs_break_callback(vnode, afs_cb_break_for_deleted);
}
}
/*
* Note that a dentry got changed. We need to set d_fsdata to the data version
* number derived from the result of the operation. It doesn't matter if
* d_fsdata goes backwards as we'll just revalidate.
*/
static inline void afs_update_dentry_version(struct afs_operation *op,
struct afs_vnode_param *dir_vp,
struct dentry *dentry)
{
if (!op->error)
dentry->d_fsdata =
(void *)(unsigned long)dir_vp->scb.status.data_version;
}
static inline int afs_io_error(struct afs_call *call, enum afs_io_error where)
{
trace_afs_io_error(call->debug_id, -EIO, where);

Просмотреть файл

@ -82,12 +82,14 @@ static int __net_init afs_net_init(struct net *net_ns)
INIT_WORK(&net->cells_manager, afs_manage_cells);
timer_setup(&net->cells_timer, afs_cells_timer, 0);
mutex_init(&net->cells_alias_lock);
mutex_init(&net->proc_cells_lock);
INIT_HLIST_HEAD(&net->proc_cells);
seqlock_init(&net->fs_lock);
net->fs_servers = RB_ROOT;
INIT_LIST_HEAD(&net->fs_updates);
INIT_LIST_HEAD(&net->fs_probe_fast);
INIT_LIST_HEAD(&net->fs_probe_slow);
INIT_HLIST_HEAD(&net->fs_proc);
INIT_HLIST_HEAD(&net->fs_addresses4);
@ -96,6 +98,8 @@ static int __net_init afs_net_init(struct net *net_ns)
INIT_WORK(&net->fs_manager, afs_manage_servers);
timer_setup(&net->fs_timer, afs_servers_timer, 0);
INIT_WORK(&net->fs_prober, afs_fs_probe_dispatcher);
timer_setup(&net->fs_probe_timer, afs_fs_probe_timer, 0);
ret = -ENOMEM;
sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);

Просмотреть файл

@ -38,7 +38,7 @@ static int afs_proc_cells_show(struct seq_file *m, void *v)
if (v == SEQ_START_TOKEN) {
/* display header on line 1 */
seq_puts(m, "USE TTL SV NAME\n");
seq_puts(m, "USE TTL SV ST NAME\n");
return 0;
}
@ -46,10 +46,11 @@ static int afs_proc_cells_show(struct seq_file *m, void *v)
vllist = rcu_dereference(cell->vl_servers);
/* display one cell per line on subsequent lines */
seq_printf(m, "%3u %6lld %2u %s\n",
seq_printf(m, "%3u %6lld %2u %2u %s\n",
atomic_read(&cell->usage),
cell->dns_expiry - ktime_get_real_seconds(),
vllist->nr_servers,
cell->state,
cell->name);
return 0;
}
@ -208,11 +209,10 @@ static const char afs_vol_types[3][3] = {
*/
static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
{
struct afs_cell *cell = PDE_DATA(file_inode(m->file));
struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link);
struct afs_volume *vol = hlist_entry(v, struct afs_volume, proc_link);
/* Display header on line 1 */
if (v == &cell->proc_volumes) {
if (v == SEQ_START_TOKEN) {
seq_puts(m, "USE VID TY NAME\n");
return 0;
}
@ -230,8 +230,8 @@ static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
{
struct afs_cell *cell = PDE_DATA(file_inode(m->file));
read_lock(&cell->proc_lock);
return seq_list_start_head(&cell->proc_volumes, *_pos);
rcu_read_lock();
return seq_hlist_start_head_rcu(&cell->proc_volumes, *_pos);
}
static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
@ -239,15 +239,13 @@ static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
{
struct afs_cell *cell = PDE_DATA(file_inode(m->file));
return seq_list_next(v, &cell->proc_volumes, _pos);
return seq_hlist_next_rcu(v, &cell->proc_volumes, _pos);
}
static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v)
__releases(cell->proc_lock)
{
struct afs_cell *cell = PDE_DATA(file_inode(m->file));
read_unlock(&cell->proc_lock);
rcu_read_unlock();
}
static const struct seq_operations afs_proc_cell_volumes_ops = {
@ -378,20 +376,26 @@ static int afs_proc_servers_show(struct seq_file *m, void *v)
int i;
if (v == SEQ_START_TOKEN) {
seq_puts(m, "UUID USE ADDR\n");
seq_puts(m, "UUID REF ACT\n");
return 0;
}
server = list_entry(v, struct afs_server, proc_link);
alist = rcu_dereference(server->addresses);
seq_printf(m, "%pU %3d %pISpc%s\n",
seq_printf(m, "%pU %3d %3d\n",
&server->uuid,
atomic_read(&server->usage),
&alist->addrs[0].transport,
alist->preferred == 0 ? "*" : "");
for (i = 1; i < alist->nr_addrs; i++)
seq_printf(m, " %pISpc%s\n",
&alist->addrs[i].transport,
atomic_read(&server->ref),
atomic_read(&server->active));
seq_printf(m, " - info: fl=%lx rtt=%u brk=%x\n",
server->flags, server->rtt, server->cb_s_break);
seq_printf(m, " - probe: last=%d out=%d\n",
(int)(jiffies - server->probed_at) / HZ,
atomic_read(&server->probe_outstanding));
seq_printf(m, " - ALIST v=%u rsp=%lx f=%lx\n",
alist->version, alist->responded, alist->failed);
for (i = 0; i < alist->nr_addrs; i++)
seq_printf(m, " [%x] %pISpc%s\n",
i, &alist->addrs[i].transport,
alist->preferred == i ? "*" : "");
return 0;
}

Просмотреть файл

@ -8,7 +8,7 @@
#define YFS_FS_SERVICE 2500
#define YFS_CM_SERVICE 2501
#define YFSCBMAX 1024
#define YFSCBMAX 1024
enum YFS_CM_Operations {
YFSCBProbe = 206, /* probe client */

Просмотреть файл

@ -14,61 +14,33 @@
#include "internal.h"
#include "afs_fs.h"
/*
* Begin an operation on the fileserver.
*
* Fileserver operations are serialised on the server by vnode, so we serialise
* them here also using the io_lock.
*/
bool afs_begin_vnode_operation(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
struct key *key, bool intr)
{
memset(fc, 0, sizeof(*fc));
fc->vnode = vnode;
fc->key = key;
fc->ac.error = SHRT_MAX;
fc->error = -EDESTADDRREQ;
if (intr) {
fc->flags |= AFS_FS_CURSOR_INTR;
if (mutex_lock_interruptible(&vnode->io_lock) < 0) {
fc->error = -EINTR;
fc->flags |= AFS_FS_CURSOR_STOP;
return false;
}
} else {
mutex_lock(&vnode->io_lock);
}
if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
fc->flags |= AFS_FS_CURSOR_CUR_ONLY;
return true;
}
/*
* Begin iteration through a server list, starting with the vnode's last used
* server if possible, or the last recorded good server if not.
*/
static bool afs_start_fs_iteration(struct afs_fs_cursor *fc,
static bool afs_start_fs_iteration(struct afs_operation *op,
struct afs_vnode *vnode)
{
struct afs_cb_interest *cbi;
struct afs_server *server;
void *cb_server;
int i;
read_lock(&vnode->volume->servers_lock);
fc->server_list = afs_get_serverlist(vnode->volume->servers);
read_unlock(&vnode->volume->servers_lock);
read_lock(&op->volume->servers_lock);
op->server_list = afs_get_serverlist(
rcu_dereference_protected(op->volume->servers,
lockdep_is_held(&op->volume->servers_lock)));
read_unlock(&op->volume->servers_lock);
fc->untried = (1UL << fc->server_list->nr_servers) - 1;
fc->index = READ_ONCE(fc->server_list->preferred);
op->untried = (1UL << op->server_list->nr_servers) - 1;
op->index = READ_ONCE(op->server_list->preferred);
cbi = rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->io_lock));
if (cbi) {
cb_server = vnode->cb_server;
if (cb_server) {
/* See if the vnode's preferred record is still available */
for (i = 0; i < fc->server_list->nr_servers; i++) {
if (fc->server_list->servers[i].cb_interest == cbi) {
fc->index = i;
for (i = 0; i < op->server_list->nr_servers; i++) {
server = op->server_list->servers[i].server;
if (server == cb_server) {
op->index = i;
goto found_interest;
}
}
@ -77,21 +49,18 @@ static bool afs_start_fs_iteration(struct afs_fs_cursor *fc,
* serving this vnode, then we can't switch to another server
* and have to return an error.
*/
if (fc->flags & AFS_FS_CURSOR_CUR_ONLY) {
fc->error = -ESTALE;
if (op->flags & AFS_OPERATION_CUR_ONLY) {
op->error = -ESTALE;
return false;
}
/* Note that the callback promise is effectively broken */
write_seqlock(&vnode->cb_lock);
ASSERTCMP(cbi, ==, rcu_access_pointer(vnode->cb_interest));
rcu_assign_pointer(vnode->cb_interest, NULL);
ASSERTCMP(cb_server, ==, vnode->cb_server);
vnode->cb_server = NULL;
if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags))
vnode->cb_break++;
write_sequnlock(&vnode->cb_lock);
afs_put_cb_interest(afs_v2net(vnode), cbi);
cbi = NULL;
}
found_interest:
@ -118,12 +87,12 @@ static void afs_busy(struct afs_volume *volume, u32 abort_code)
/*
* Sleep and retry the operation to the same fileserver.
*/
static bool afs_sleep_and_retry(struct afs_fs_cursor *fc)
static bool afs_sleep_and_retry(struct afs_operation *op)
{
if (fc->flags & AFS_FS_CURSOR_INTR) {
if (!(op->flags & AFS_OPERATION_UNINTR)) {
msleep_interruptible(1000);
if (signal_pending(current)) {
fc->error = -ERESTARTSYS;
op->error = -ERESTARTSYS;
return false;
}
} else {
@ -137,26 +106,26 @@ static bool afs_sleep_and_retry(struct afs_fs_cursor *fc)
* Select the fileserver to use. May be called multiple times to rotate
* through the fileservers.
*/
bool afs_select_fileserver(struct afs_fs_cursor *fc)
bool afs_select_fileserver(struct afs_operation *op)
{
struct afs_addr_list *alist;
struct afs_server *server;
struct afs_vnode *vnode = fc->vnode;
struct afs_vnode *vnode = op->file[0].vnode;
struct afs_error e;
u32 rtt;
int error = fc->ac.error, i;
int error = op->ac.error, i;
_enter("%lx[%d],%lx[%d],%d,%d",
fc->untried, fc->index,
fc->ac.tried, fc->ac.index,
error, fc->ac.abort_code);
op->untried, op->index,
op->ac.tried, op->ac.index,
error, op->ac.abort_code);
if (fc->flags & AFS_FS_CURSOR_STOP) {
if (op->flags & AFS_OPERATION_STOP) {
_leave(" = f [stopped]");
return false;
}
fc->nr_iterations++;
op->nr_iterations++;
/* Evaluate the result of the previous operation, if there was one. */
switch (error) {
@ -166,8 +135,8 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
case 0:
default:
/* Success or local failure. Stop. */
fc->error = error;
fc->flags |= AFS_FS_CURSOR_STOP;
op->error = error;
op->flags |= AFS_OPERATION_STOP;
_leave(" = f [okay/local %d]", error);
return false;
@ -175,42 +144,42 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
/* The far side rejected the operation on some grounds. This
* might involve the server being busy or the volume having been moved.
*/
switch (fc->ac.abort_code) {
switch (op->ac.abort_code) {
case VNOVOL:
/* This fileserver doesn't know about the volume.
* - May indicate that the VL is wrong - retry once and compare
* the results.
* - May indicate that the fileserver couldn't attach to the vol.
*/
if (fc->flags & AFS_FS_CURSOR_VNOVOL) {
fc->error = -EREMOTEIO;
if (op->flags & AFS_OPERATION_VNOVOL) {
op->error = -EREMOTEIO;
goto next_server;
}
write_lock(&vnode->volume->servers_lock);
fc->server_list->vnovol_mask |= 1 << fc->index;
write_unlock(&vnode->volume->servers_lock);
write_lock(&op->volume->servers_lock);
op->server_list->vnovol_mask |= 1 << op->index;
write_unlock(&op->volume->servers_lock);
set_bit(AFS_VOLUME_NEEDS_UPDATE, &vnode->volume->flags);
error = afs_check_volume_status(vnode->volume, fc);
set_bit(AFS_VOLUME_NEEDS_UPDATE, &op->volume->flags);
error = afs_check_volume_status(op->volume, op);
if (error < 0)
goto failed_set_error;
if (test_bit(AFS_VOLUME_DELETED, &vnode->volume->flags)) {
fc->error = -ENOMEDIUM;
if (test_bit(AFS_VOLUME_DELETED, &op->volume->flags)) {
op->error = -ENOMEDIUM;
goto failed;
}
/* If the server list didn't change, then assume that
* it's the fileserver having trouble.
*/
if (vnode->volume->servers == fc->server_list) {
fc->error = -EREMOTEIO;
if (rcu_access_pointer(op->volume->servers) == op->server_list) {
op->error = -EREMOTEIO;
goto next_server;
}
/* Try again */
fc->flags |= AFS_FS_CURSOR_VNOVOL;
op->flags |= AFS_OPERATION_VNOVOL;
_leave(" = t [vnovol]");
return true;
@ -220,20 +189,20 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
case VONLINE:
case VDISKFULL:
case VOVERQUOTA:
fc->error = afs_abort_to_error(fc->ac.abort_code);
op->error = afs_abort_to_error(op->ac.abort_code);
goto next_server;
case VOFFLINE:
if (!test_and_set_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags)) {
afs_busy(vnode->volume, fc->ac.abort_code);
clear_bit(AFS_VOLUME_BUSY, &vnode->volume->flags);
if (!test_and_set_bit(AFS_VOLUME_OFFLINE, &op->volume->flags)) {
afs_busy(op->volume, op->ac.abort_code);
clear_bit(AFS_VOLUME_BUSY, &op->volume->flags);
}
if (fc->flags & AFS_FS_CURSOR_NO_VSLEEP) {
fc->error = -EADV;
if (op->flags & AFS_OPERATION_NO_VSLEEP) {
op->error = -EADV;
goto failed;
}
if (fc->flags & AFS_FS_CURSOR_CUR_ONLY) {
fc->error = -ESTALE;
if (op->flags & AFS_OPERATION_CUR_ONLY) {
op->error = -ESTALE;
goto failed;
}
goto busy;
@ -244,17 +213,17 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
/* Retry after going round all the servers unless we
* have a file lock we need to maintain.
*/
if (fc->flags & AFS_FS_CURSOR_NO_VSLEEP) {
fc->error = -EBUSY;
if (op->flags & AFS_OPERATION_NO_VSLEEP) {
op->error = -EBUSY;
goto failed;
}
if (!test_and_set_bit(AFS_VOLUME_BUSY, &vnode->volume->flags)) {
afs_busy(vnode->volume, fc->ac.abort_code);
clear_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags);
if (!test_and_set_bit(AFS_VOLUME_BUSY, &op->volume->flags)) {
afs_busy(op->volume, op->ac.abort_code);
clear_bit(AFS_VOLUME_OFFLINE, &op->volume->flags);
}
busy:
if (fc->flags & AFS_FS_CURSOR_CUR_ONLY) {
if (!afs_sleep_and_retry(fc))
if (op->flags & AFS_OPERATION_CUR_ONLY) {
if (!afs_sleep_and_retry(op))
goto failed;
/* Retry with same server & address */
@ -262,7 +231,7 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
return true;
}
fc->flags |= AFS_FS_CURSOR_VBUSY;
op->flags |= AFS_OPERATION_VBUSY;
goto next_server;
case VMOVED:
@ -273,15 +242,15 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
* We also limit the number of VMOVED hops we will
* honour, just in case someone sets up a loop.
*/
if (fc->flags & AFS_FS_CURSOR_VMOVED) {
fc->error = -EREMOTEIO;
if (op->flags & AFS_OPERATION_VMOVED) {
op->error = -EREMOTEIO;
goto failed;
}
fc->flags |= AFS_FS_CURSOR_VMOVED;
op->flags |= AFS_OPERATION_VMOVED;
set_bit(AFS_VOLUME_WAIT, &vnode->volume->flags);
set_bit(AFS_VOLUME_NEEDS_UPDATE, &vnode->volume->flags);
error = afs_check_volume_status(vnode->volume, fc);
set_bit(AFS_VOLUME_WAIT, &op->volume->flags);
set_bit(AFS_VOLUME_NEEDS_UPDATE, &op->volume->flags);
error = afs_check_volume_status(op->volume, op);
if (error < 0)
goto failed_set_error;
@ -294,23 +263,23 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
*
* TODO: Retry a few times with sleeps.
*/
if (vnode->volume->servers == fc->server_list) {
fc->error = -ENOMEDIUM;
if (rcu_access_pointer(op->volume->servers) == op->server_list) {
op->error = -ENOMEDIUM;
goto failed;
}
goto restart_from_beginning;
default:
clear_bit(AFS_VOLUME_OFFLINE, &vnode->volume->flags);
clear_bit(AFS_VOLUME_BUSY, &vnode->volume->flags);
fc->error = afs_abort_to_error(fc->ac.abort_code);
clear_bit(AFS_VOLUME_OFFLINE, &op->volume->flags);
clear_bit(AFS_VOLUME_BUSY, &op->volume->flags);
op->error = afs_abort_to_error(op->ac.abort_code);
goto failed;
}
case -ETIMEDOUT:
case -ETIME:
if (fc->error != -EDESTADDRREQ)
if (op->error != -EDESTADDRREQ)
goto iterate_address;
/* Fall through */
case -ERFKILL:
@ -320,103 +289,94 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
case -EHOSTDOWN:
case -ECONNREFUSED:
_debug("no conn");
fc->error = error;
op->error = error;
goto iterate_address;
case -ECONNRESET:
_debug("call reset");
fc->error = error;
op->error = error;
goto failed;
}
restart_from_beginning:
_debug("restart");
afs_end_cursor(&fc->ac);
afs_put_cb_interest(afs_v2net(vnode), fc->cbi);
fc->cbi = NULL;
afs_put_serverlist(afs_v2net(vnode), fc->server_list);
fc->server_list = NULL;
afs_end_cursor(&op->ac);
op->server = NULL;
afs_put_serverlist(op->net, op->server_list);
op->server_list = NULL;
start:
_debug("start");
/* See if we need to do an update of the volume record. Note that the
* volume may have moved or even have been deleted.
*/
error = afs_check_volume_status(vnode->volume, fc);
error = afs_check_volume_status(op->volume, op);
if (error < 0)
goto failed_set_error;
if (!afs_start_fs_iteration(fc, vnode))
if (!afs_start_fs_iteration(op, vnode))
goto failed;
_debug("__ VOL %llx __", vnode->volume->vid);
error = afs_probe_fileservers(afs_v2net(vnode), fc->key, fc->server_list);
if (error < 0)
goto failed_set_error;
_debug("__ VOL %llx __", op->volume->vid);
pick_server:
_debug("pick [%lx]", fc->untried);
_debug("pick [%lx]", op->untried);
error = afs_wait_for_fs_probes(fc->server_list, fc->untried);
error = afs_wait_for_fs_probes(op->server_list, op->untried);
if (error < 0)
goto failed_set_error;
/* Pick the untried server with the lowest RTT. If we have outstanding
* callbacks, we stick with the server we're already using if we can.
*/
if (fc->cbi) {
_debug("cbi %u", fc->index);
if (test_bit(fc->index, &fc->untried))
if (op->server) {
_debug("server %u", op->index);
if (test_bit(op->index, &op->untried))
goto selected_server;
afs_put_cb_interest(afs_v2net(vnode), fc->cbi);
fc->cbi = NULL;
_debug("nocbi");
op->server = NULL;
_debug("no server");
}
fc->index = -1;
op->index = -1;
rtt = U32_MAX;
for (i = 0; i < fc->server_list->nr_servers; i++) {
struct afs_server *s = fc->server_list->servers[i].server;
for (i = 0; i < op->server_list->nr_servers; i++) {
struct afs_server *s = op->server_list->servers[i].server;
if (!test_bit(i, &fc->untried) || !s->probe.responded)
if (!test_bit(i, &op->untried) ||
!test_bit(AFS_SERVER_FL_RESPONDING, &s->flags))
continue;
if (s->probe.rtt < rtt) {
fc->index = i;
op->index = i;
rtt = s->probe.rtt;
}
}
if (fc->index == -1)
if (op->index == -1)
goto no_more_servers;
selected_server:
_debug("use %d", fc->index);
__clear_bit(fc->index, &fc->untried);
_debug("use %d", op->index);
__clear_bit(op->index, &op->untried);
/* We're starting on a different fileserver from the list. We need to
* check it, create a callback intercept, find its address list and
* probe its capabilities before we use it.
*/
ASSERTCMP(fc->ac.alist, ==, NULL);
server = fc->server_list->servers[fc->index].server;
ASSERTCMP(op->ac.alist, ==, NULL);
server = op->server_list->servers[op->index].server;
if (!afs_check_server_record(fc, server))
if (!afs_check_server_record(op, server))
goto failed;
_debug("USING SERVER: %pU", &server->uuid);
/* Make sure we've got a callback interest record for this server. We
* have to link it in before we send the request as we can be sent a
* break request before we've finished decoding the reply and
* installing the vnode.
*/
error = afs_register_server_cb_interest(vnode, fc->server_list,
fc->index);
if (error < 0)
goto failed_set_error;
fc->cbi = afs_get_cb_interest(
rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->io_lock)));
op->flags |= AFS_OPERATION_RETRY_SERVER;
op->server = server;
if (vnode->cb_server != server) {
vnode->cb_server = server;
vnode->cb_s_break = server->cb_s_break;
vnode->cb_v_break = vnode->volume->cb_v_break;
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
}
read_lock(&server->fs_lock);
alist = rcu_dereference_protected(server->addresses,
@ -424,44 +384,68 @@ selected_server:
afs_get_addrlist(alist);
read_unlock(&server->fs_lock);
memset(&fc->ac, 0, sizeof(fc->ac));
retry_server:
memset(&op->ac, 0, sizeof(op->ac));
if (!fc->ac.alist)
fc->ac.alist = alist;
if (!op->ac.alist)
op->ac.alist = alist;
else
afs_put_addrlist(alist);
fc->ac.index = -1;
op->ac.index = -1;
iterate_address:
ASSERT(fc->ac.alist);
ASSERT(op->ac.alist);
/* Iterate over the current server's address list to try and find an
* address on which it will respond to us.
*/
if (!afs_iterate_addresses(&fc->ac))
goto next_server;
if (!afs_iterate_addresses(&op->ac))
goto out_of_addresses;
_debug("address [%u] %u/%u", fc->index, fc->ac.index, fc->ac.alist->nr_addrs);
_debug("address [%u] %u/%u %pISp",
op->index, op->ac.index, op->ac.alist->nr_addrs,
&op->ac.alist->addrs[op->ac.index].transport);
_leave(" = t");
return true;
out_of_addresses:
/* We've now had a failure to respond on all of a server's addresses -
* immediately probe them again and consider retrying the server.
*/
afs_probe_fileserver(op->net, op->server);
if (op->flags & AFS_OPERATION_RETRY_SERVER) {
alist = op->ac.alist;
error = afs_wait_for_one_fs_probe(
op->server, !(op->flags & AFS_OPERATION_UNINTR));
switch (error) {
case 0:
op->flags &= ~AFS_OPERATION_RETRY_SERVER;
goto retry_server;
case -ERESTARTSYS:
goto failed_set_error;
case -ETIME:
case -EDESTADDRREQ:
goto next_server;
}
}
next_server:
_debug("next");
afs_end_cursor(&fc->ac);
afs_end_cursor(&op->ac);
goto pick_server;
no_more_servers:
/* That's all the servers poked to no good effect. Try again if some
* of them were busy.
*/
if (fc->flags & AFS_FS_CURSOR_VBUSY)
if (op->flags & AFS_OPERATION_VBUSY)
goto restart_from_beginning;
e.error = -EDESTADDRREQ;
e.responded = false;
for (i = 0; i < fc->server_list->nr_servers; i++) {
struct afs_server *s = fc->server_list->servers[i].server;
for (i = 0; i < op->server_list->nr_servers; i++) {
struct afs_server *s = op->server_list->servers[i].server;
afs_prioritise_error(&e, READ_ONCE(s->probe.error),
s->probe.abort_code);
@ -470,101 +454,18 @@ no_more_servers:
error = e.error;
failed_set_error:
fc->error = error;
op->error = error;
failed:
fc->flags |= AFS_FS_CURSOR_STOP;
afs_end_cursor(&fc->ac);
_leave(" = f [failed %d]", fc->error);
return false;
}
/*
* Select the same fileserver we used for a vnode before and only that
* fileserver. We use this when we have a lock on that file, which is backed
* only by the fileserver we obtained it from.
*/
bool afs_select_current_fileserver(struct afs_fs_cursor *fc)
{
struct afs_vnode *vnode = fc->vnode;
struct afs_cb_interest *cbi;
struct afs_addr_list *alist;
int error = fc->ac.error;
_enter("");
cbi = rcu_dereference_protected(vnode->cb_interest,
lockdep_is_held(&vnode->io_lock));
switch (error) {
case SHRT_MAX:
if (!cbi) {
fc->error = -ESTALE;
fc->flags |= AFS_FS_CURSOR_STOP;
return false;
}
fc->cbi = afs_get_cb_interest(cbi);
read_lock(&cbi->server->fs_lock);
alist = rcu_dereference_protected(cbi->server->addresses,
lockdep_is_held(&cbi->server->fs_lock));
afs_get_addrlist(alist);
read_unlock(&cbi->server->fs_lock);
if (!alist) {
fc->error = -ESTALE;
fc->flags |= AFS_FS_CURSOR_STOP;
return false;
}
memset(&fc->ac, 0, sizeof(fc->ac));
fc->ac.alist = alist;
fc->ac.index = -1;
goto iterate_address;
case 0:
default:
/* Success or local failure. Stop. */
fc->error = error;
fc->flags |= AFS_FS_CURSOR_STOP;
_leave(" = f [okay/local %d]", error);
return false;
case -ECONNABORTED:
fc->error = afs_abort_to_error(fc->ac.abort_code);
fc->flags |= AFS_FS_CURSOR_STOP;
_leave(" = f [abort]");
return false;
case -ERFKILL:
case -EADDRNOTAVAIL:
case -ENETUNREACH:
case -EHOSTUNREACH:
case -EHOSTDOWN:
case -ECONNREFUSED:
case -ETIMEDOUT:
case -ETIME:
_debug("no conn");
fc->error = error;
goto iterate_address;
}
iterate_address:
/* Iterate over the current server's address list to try and find an
* address on which it will respond to us.
*/
if (afs_iterate_addresses(&fc->ac)) {
_leave(" = t");
return true;
}
afs_end_cursor(&fc->ac);
op->flags |= AFS_OPERATION_STOP;
afs_end_cursor(&op->ac);
_leave(" = f [failed %d]", op->error);
return false;
}
/*
* Dump cursor state in the case of the error being EDESTADDRREQ.
*/
static void afs_dump_edestaddrreq(const struct afs_fs_cursor *fc)
void afs_dump_edestaddrreq(const struct afs_operation *op)
{
static int count;
int i;
@ -576,13 +477,14 @@ static void afs_dump_edestaddrreq(const struct afs_fs_cursor *fc)
rcu_read_lock();
pr_notice("EDESTADDR occurred\n");
pr_notice("FC: cbb=%x cbb2=%x fl=%hx err=%hd\n",
fc->cb_break, fc->cb_break_2, fc->flags, fc->error);
pr_notice("FC: cbb=%x cbb2=%x fl=%x err=%hd\n",
op->file[0].cb_break_before,
op->file[1].cb_break_before, op->flags, op->error);
pr_notice("FC: ut=%lx ix=%d ni=%u\n",
fc->untried, fc->index, fc->nr_iterations);
op->untried, op->index, op->nr_iterations);
if (fc->server_list) {
const struct afs_server_list *sl = fc->server_list;
if (op->server_list) {
const struct afs_server_list *sl = op->server_list;
pr_notice("FC: SL nr=%u pr=%u vnov=%hx\n",
sl->nr_servers, sl->preferred, sl->vnovol_mask);
for (i = 0; i < sl->nr_servers; i++) {
@ -596,41 +498,16 @@ static void afs_dump_edestaddrreq(const struct afs_fs_cursor *fc)
a->version,
a->nr_ipv4, a->nr_addrs, a->max_addrs,
a->preferred);
pr_notice("FC: - pr=%lx R=%lx F=%lx\n",
a->probed, a->responded, a->failed);
if (a == fc->ac.alist)
pr_notice("FC: - R=%lx F=%lx\n",
a->responded, a->failed);
if (a == op->ac.alist)
pr_notice("FC: - current\n");
}
}
}
pr_notice("AC: t=%lx ax=%u ac=%d er=%d r=%u ni=%u\n",
fc->ac.tried, fc->ac.index, fc->ac.abort_code, fc->ac.error,
fc->ac.responded, fc->ac.nr_iterations);
op->ac.tried, op->ac.index, op->ac.abort_code, op->ac.error,
op->ac.responded, op->ac.nr_iterations);
rcu_read_unlock();
}
/*
* Tidy up a filesystem cursor and unlock the vnode.
*/
int afs_end_vnode_operation(struct afs_fs_cursor *fc)
{
struct afs_net *net = afs_v2net(fc->vnode);
if (fc->error == -EDESTADDRREQ ||
fc->error == -EADDRNOTAVAIL ||
fc->error == -ENETUNREACH ||
fc->error == -EHOSTUNREACH)
afs_dump_edestaddrreq(fc);
mutex_unlock(&fc->vnode->io_lock);
afs_end_cursor(&fc->ac);
afs_put_cb_interest(net, fc->cbi);
afs_put_serverlist(net, fc->server_list);
if (fc->error == -ECONNABORTED)
fc->error = afs_abort_to_error(fc->ac.abort_code);
return fc->error;
}

Просмотреть файл

@ -181,8 +181,7 @@ void afs_put_call(struct afs_call *call)
if (call->type->destructor)
call->type->destructor(call);
afs_put_server(call->net, call->server, afs_server_trace_put_call);
afs_put_cb_interest(call->net, call->cbi);
afs_unuse_server_notime(call->net, call->server, afs_server_trace_put_call);
afs_put_addrlist(call->alist);
kfree(call->request);
@ -281,18 +280,19 @@ static void afs_load_bvec(struct afs_call *call, struct msghdr *msg,
struct bio_vec *bv, pgoff_t first, pgoff_t last,
unsigned offset)
{
struct afs_operation *op = call->op;
struct page *pages[AFS_BVEC_MAX];
unsigned int nr, n, i, to, bytes = 0;
nr = min_t(pgoff_t, last - first + 1, AFS_BVEC_MAX);
n = find_get_pages_contig(call->mapping, first, nr, pages);
n = find_get_pages_contig(op->store.mapping, first, nr, pages);
ASSERTCMP(n, ==, nr);
msg->msg_flags |= MSG_MORE;
for (i = 0; i < nr; i++) {
to = PAGE_SIZE;
if (first + i >= last) {
to = call->last_to;
to = op->store.last_to;
msg->msg_flags &= ~MSG_MORE;
}
bv[i].bv_page = pages[i];
@ -322,13 +322,14 @@ static void afs_notify_end_request_tx(struct sock *sock,
*/
static int afs_send_pages(struct afs_call *call, struct msghdr *msg)
{
struct afs_operation *op = call->op;
struct bio_vec bv[AFS_BVEC_MAX];
unsigned int bytes, nr, loop, offset;
pgoff_t first = call->first, last = call->last;
pgoff_t first = op->store.first, last = op->store.last;
int ret;
offset = call->first_offset;
call->first_offset = 0;
offset = op->store.first_offset;
op->store.first_offset = 0;
do {
afs_load_bvec(call, msg, bv, first, last, offset);
@ -338,7 +339,7 @@ static int afs_send_pages(struct afs_call *call, struct msghdr *msg)
bytes = msg->msg_iter.count;
nr = msg->msg_iter.nr_segs;
ret = rxrpc_kernel_send_data(call->net->socket, call->rxcall, msg,
ret = rxrpc_kernel_send_data(op->net->socket, call->rxcall, msg,
bytes, afs_notify_end_request_tx);
for (loop = 0; loop < nr; loop++)
put_page(bv[loop].bv_page);
@ -348,7 +349,7 @@ static int afs_send_pages(struct afs_call *call, struct msghdr *msg)
first += nr;
} while (first <= last);
trace_afs_sent_pages(call, call->first, last, first, ret);
trace_afs_sent_pages(call, op->store.first, last, first, ret);
return ret;
}
@ -383,16 +384,18 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
*/
tx_total_len = call->request_size;
if (call->send_pages) {
if (call->last == call->first) {
tx_total_len += call->last_to - call->first_offset;
struct afs_operation *op = call->op;
if (op->store.last == op->store.first) {
tx_total_len += op->store.last_to - op->store.first_offset;
} else {
/* It looks mathematically like you should be able to
* combine the following lines with the ones above, but
* unsigned arithmetic is fun when it wraps...
*/
tx_total_len += PAGE_SIZE - call->first_offset;
tx_total_len += call->last_to;
tx_total_len += (call->last - call->first - 1) * PAGE_SIZE;
tx_total_len += PAGE_SIZE - op->store.first_offset;
tx_total_len += op->store.last_to;
tx_total_len += (op->store.last - op->store.first - 1) * PAGE_SIZE;
}
}
@ -538,13 +541,15 @@ static void afs_deliver_to_call(struct afs_call *call)
ret = call->type->deliver(call);
state = READ_ONCE(call->state);
if (ret == 0 && call->unmarshalling_error)
ret = -EBADMSG;
switch (ret) {
case 0:
afs_queue_call_work(call);
if (state == AFS_CALL_CL_PROC_REPLY) {
if (call->cbi)
if (call->op)
set_bit(AFS_SERVER_FL_MAY_HAVE_CB,
&call->cbi->server->flags);
&call->op->server->flags);
goto call_complete;
}
ASSERTCMP(state, >, AFS_CALL_CL_PROC_REPLY);
@ -957,9 +962,11 @@ int afs_extract_data(struct afs_call *call, bool want_more)
/*
* Log protocol error production.
*/
noinline int afs_protocol_error(struct afs_call *call, int error,
noinline int afs_protocol_error(struct afs_call *call,
enum afs_eproto_cause cause)
{
trace_afs_protocol_error(call, error, cause);
return error;
trace_afs_protocol_error(call, cause);
if (call)
call->unmarshalling_error = true;
return -EBADMSG;
}

Просмотреть файл

@ -170,8 +170,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
break;
}
if (afs_cb_is_broken(cb_break, vnode,
rcu_dereference(vnode->cb_interest))) {
if (afs_cb_is_broken(cb_break, vnode)) {
changed = true;
break;
}
@ -201,7 +200,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
}
}
if (afs_cb_is_broken(cb_break, vnode, rcu_dereference(vnode->cb_interest)))
if (afs_cb_is_broken(cb_break, vnode))
goto someone_else_changed_it;
/* We need a ref on any permits list we want to copy as we'll have to
@ -281,8 +280,7 @@ found:
rcu_read_lock();
spin_lock(&vnode->lock);
zap = rcu_access_pointer(vnode->permit_cache);
if (!afs_cb_is_broken(cb_break, vnode, rcu_dereference(vnode->cb_interest)) &&
zap == permits)
if (!afs_cb_is_broken(cb_break, vnode) && zap == permits)
rcu_assign_pointer(vnode->permit_cache, replacement);
else
zap = replacement;

Просмотреть файл

@ -12,19 +12,11 @@
#include "protocol_yfs.h"
static unsigned afs_server_gc_delay = 10; /* Server record timeout in seconds */
static unsigned afs_server_update_delay = 30; /* Time till VLDB recheck in secs */
static atomic_t afs_server_debug_id;
static void afs_inc_servers_outstanding(struct afs_net *net)
{
atomic_inc(&net->servers_outstanding);
}
static void afs_dec_servers_outstanding(struct afs_net *net)
{
if (atomic_dec_and_test(&net->servers_outstanding))
wake_up_var(&net->servers_outstanding);
}
static struct afs_server *afs_maybe_use_server(struct afs_server *,
enum afs_server_trace);
static void __afs_put_server(struct afs_net *, struct afs_server *);
/*
* Find a server by one of its addresses.
@ -41,7 +33,7 @@ struct afs_server *afs_find_server(struct afs_net *net,
do {
if (server)
afs_put_server(net, server, afs_server_trace_put_find_rsq);
afs_unuse_server_notime(net, server, afs_server_trace_put_find_rsq);
server = NULL;
read_seqbegin_or_lock(&net->fs_addr_lock, &seq);
@ -79,9 +71,9 @@ struct afs_server *afs_find_server(struct afs_net *net,
}
server = NULL;
continue;
found:
if (server && !atomic_inc_not_zero(&server->usage))
server = NULL;
server = afs_maybe_use_server(server, afs_server_trace_get_by_addr);
} while (need_seqretry(&net->fs_addr_lock, seq));
@ -92,7 +84,7 @@ struct afs_server *afs_find_server(struct afs_net *net,
}
/*
* Look up a server by its UUID
* Look up a server by its UUID and mark it active.
*/
struct afs_server *afs_find_server_by_uuid(struct afs_net *net, const uuid_t *uuid)
{
@ -108,7 +100,7 @@ struct afs_server *afs_find_server_by_uuid(struct afs_net *net, const uuid_t *uu
* changes.
*/
if (server)
afs_put_server(net, server, afs_server_trace_put_uuid_rsq);
afs_unuse_server(net, server, afs_server_trace_put_uuid_rsq);
server = NULL;
read_seqbegin_or_lock(&net->fs_lock, &seq);
@ -123,7 +115,7 @@ struct afs_server *afs_find_server_by_uuid(struct afs_net *net, const uuid_t *uu
} else if (diff > 0) {
p = p->rb_right;
} else {
afs_get_server(server, afs_server_trace_get_by_uuid);
afs_use_server(server, afs_server_trace_get_by_uuid);
break;
}
@ -138,13 +130,16 @@ struct afs_server *afs_find_server_by_uuid(struct afs_net *net, const uuid_t *uu
}
/*
* Install a server record in the namespace tree
* Install a server record in the namespace tree. If there's a clash, we stick
* it into a list anchored on whichever afs_server struct is actually in the
* tree.
*/
static struct afs_server *afs_install_server(struct afs_net *net,
static struct afs_server *afs_install_server(struct afs_cell *cell,
struct afs_server *candidate)
{
const struct afs_addr_list *alist;
struct afs_server *server;
struct afs_server *server, *next;
struct afs_net *net = cell->net;
struct rb_node **pp, *p;
int diff;
@ -160,12 +155,30 @@ static struct afs_server *afs_install_server(struct afs_net *net,
_debug("- consider %p", p);
server = rb_entry(p, struct afs_server, uuid_rb);
diff = memcmp(&candidate->uuid, &server->uuid, sizeof(uuid_t));
if (diff < 0)
if (diff < 0) {
pp = &(*pp)->rb_left;
else if (diff > 0)
} else if (diff > 0) {
pp = &(*pp)->rb_right;
else
goto exists;
} else {
if (server->cell == cell)
goto exists;
/* We have the same UUID representing servers in
* different cells. Append the new server to the list.
*/
for (;;) {
next = rcu_dereference_protected(
server->uuid_next,
lockdep_is_held(&net->fs_lock.lock));
if (!next)
break;
server = next;
}
rcu_assign_pointer(server->uuid_next, candidate);
candidate->uuid_prev = server;
server = candidate;
goto added_dup;
}
}
server = candidate;
@ -173,6 +186,7 @@ static struct afs_server *afs_install_server(struct afs_net *net,
rb_insert_color(&server->uuid_rb, &net->fs_servers);
hlist_add_head_rcu(&server->proc_link, &net->fs_proc);
added_dup:
write_seqlock(&net->fs_addr_lock);
alist = rcu_dereference_protected(server->addresses,
lockdep_is_held(&net->fs_addr_lock.lock));
@ -199,13 +213,14 @@ exists:
}
/*
* allocate a new server record
* Allocate a new server record and mark it active.
*/
static struct afs_server *afs_alloc_server(struct afs_net *net,
static struct afs_server *afs_alloc_server(struct afs_cell *cell,
const uuid_t *uuid,
struct afs_addr_list *alist)
{
struct afs_server *server;
struct afs_net *net = cell->net;
_enter("");
@ -213,20 +228,21 @@ static struct afs_server *afs_alloc_server(struct afs_net *net,
if (!server)
goto enomem;
atomic_set(&server->usage, 1);
atomic_set(&server->ref, 1);
atomic_set(&server->active, 1);
server->debug_id = atomic_inc_return(&afs_server_debug_id);
RCU_INIT_POINTER(server->addresses, alist);
server->addr_version = alist->version;
server->uuid = *uuid;
server->update_at = ktime_get_real_seconds() + afs_server_update_delay;
rwlock_init(&server->fs_lock);
INIT_HLIST_HEAD(&server->cb_volumes);
rwlock_init(&server->cb_break_lock);
init_waitqueue_head(&server->probe_wq);
INIT_LIST_HEAD(&server->probe_link);
spin_lock_init(&server->probe_lock);
server->cell = cell;
server->rtt = UINT_MAX;
afs_inc_servers_outstanding(net);
trace_afs_server(server, 1, afs_server_trace_alloc);
trace_afs_server(server, 1, 1, afs_server_trace_alloc);
_leave(" = %p", server);
return server;
@ -264,7 +280,7 @@ static struct afs_addr_list *afs_vl_lookup_addrs(struct afs_cell *cell,
* Get or create a fileserver record.
*/
struct afs_server *afs_lookup_server(struct afs_cell *cell, struct key *key,
const uuid_t *uuid)
const uuid_t *uuid, u32 addr_version)
{
struct afs_addr_list *alist;
struct afs_server *server, *candidate;
@ -272,26 +288,34 @@ struct afs_server *afs_lookup_server(struct afs_cell *cell, struct key *key,
_enter("%p,%pU", cell->net, uuid);
server = afs_find_server_by_uuid(cell->net, uuid);
if (server)
if (server) {
if (server->addr_version != addr_version)
set_bit(AFS_SERVER_FL_NEEDS_UPDATE, &server->flags);
return server;
}
alist = afs_vl_lookup_addrs(cell, key, uuid);
if (IS_ERR(alist))
return ERR_CAST(alist);
candidate = afs_alloc_server(cell->net, uuid, alist);
candidate = afs_alloc_server(cell, uuid, alist);
if (!candidate) {
afs_put_addrlist(alist);
return ERR_PTR(-ENOMEM);
}
server = afs_install_server(cell->net, candidate);
server = afs_install_server(cell, candidate);
if (server != candidate) {
afs_put_addrlist(alist);
kfree(candidate);
} else {
/* Immediately dispatch an asynchronous probe to each interface
* on the fileserver. This will make sure the repeat-probing
* service is started.
*/
afs_fs_probe_fileserver(cell->net, server, key, true);
}
_leave(" = %p{%d}", server, atomic_read(&server->usage));
return server;
}
@ -327,9 +351,38 @@ void afs_servers_timer(struct timer_list *timer)
struct afs_server *afs_get_server(struct afs_server *server,
enum afs_server_trace reason)
{
unsigned int u = atomic_inc_return(&server->usage);
unsigned int u = atomic_inc_return(&server->ref);
trace_afs_server(server, u, reason);
trace_afs_server(server, u, atomic_read(&server->active), reason);
return server;
}
/*
* Try to get a reference on a server object.
*/
static struct afs_server *afs_maybe_use_server(struct afs_server *server,
enum afs_server_trace reason)
{
unsigned int r = atomic_fetch_add_unless(&server->ref, 1, 0);
unsigned int a;
if (r == 0)
return NULL;
a = atomic_inc_return(&server->active);
trace_afs_server(server, r, a, reason);
return server;
}
/*
* Get an active count on a server object.
*/
struct afs_server *afs_use_server(struct afs_server *server, enum afs_server_trace reason)
{
unsigned int r = atomic_inc_return(&server->ref);
unsigned int a = atomic_inc_return(&server->active);
trace_afs_server(server, r, a, reason);
return server;
}
@ -344,32 +397,57 @@ void afs_put_server(struct afs_net *net, struct afs_server *server,
if (!server)
return;
server->put_time = ktime_get_real_seconds();
usage = atomic_dec_return(&server->ref);
trace_afs_server(server, usage, atomic_read(&server->active), reason);
if (unlikely(usage == 0))
__afs_put_server(net, server);
}
usage = atomic_dec_return(&server->usage);
/*
* Drop an active count on a server object without updating the last-unused
* time.
*/
void afs_unuse_server_notime(struct afs_net *net, struct afs_server *server,
enum afs_server_trace reason)
{
if (server) {
unsigned int active = atomic_dec_return(&server->active);
trace_afs_server(server, usage, reason);
if (active == 0)
afs_set_server_timer(net, afs_server_gc_delay);
afs_put_server(net, server, reason);
}
}
if (likely(usage > 0))
return;
afs_set_server_timer(net, afs_server_gc_delay);
/*
* Drop an active count on a server object.
*/
void afs_unuse_server(struct afs_net *net, struct afs_server *server,
enum afs_server_trace reason)
{
if (server) {
server->unuse_time = ktime_get_real_seconds();
afs_unuse_server_notime(net, server, reason);
}
}
static void afs_server_rcu(struct rcu_head *rcu)
{
struct afs_server *server = container_of(rcu, struct afs_server, rcu);
trace_afs_server(server, atomic_read(&server->usage),
afs_server_trace_free);
trace_afs_server(server, atomic_read(&server->ref),
atomic_read(&server->active), afs_server_trace_free);
afs_put_addrlist(rcu_access_pointer(server->addresses));
kfree(server);
}
/*
* destroy a dead server
*/
static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
static void __afs_put_server(struct afs_net *net, struct afs_server *server)
{
call_rcu(&server->rcu, afs_server_rcu);
afs_dec_servers_outstanding(net);
}
static void afs_give_up_callbacks(struct afs_net *net, struct afs_server *server)
{
struct afs_addr_list *alist = rcu_access_pointer(server->addresses);
struct afs_addr_cursor ac = {
@ -378,19 +456,18 @@ static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
.error = 0,
};
trace_afs_server(server, atomic_read(&server->usage),
afs_server_trace_give_up_cb);
afs_fs_give_up_all_callbacks(net, server, &ac, NULL);
}
/*
* destroy a dead server
*/
static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
{
if (test_bit(AFS_SERVER_FL_MAY_HAVE_CB, &server->flags))
afs_fs_give_up_all_callbacks(net, server, &ac, NULL);
afs_give_up_callbacks(net, server);
wait_var_event(&server->probe_outstanding,
atomic_read(&server->probe_outstanding) == 0);
trace_afs_server(server, atomic_read(&server->usage),
afs_server_trace_destroy);
call_rcu(&server->rcu, afs_server_rcu);
afs_dec_servers_outstanding(net);
afs_put_server(net, server, afs_server_trace_destroy);
}
/*
@ -398,32 +475,49 @@ static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
*/
static void afs_gc_servers(struct afs_net *net, struct afs_server *gc_list)
{
struct afs_server *server;
bool deleted;
int usage;
struct afs_server *server, *next, *prev;
int active;
while ((server = gc_list)) {
gc_list = server->gc_next;
write_seqlock(&net->fs_lock);
usage = 1;
deleted = atomic_try_cmpxchg(&server->usage, &usage, 0);
trace_afs_server(server, usage, afs_server_trace_gc);
if (deleted) {
rb_erase(&server->uuid_rb, &net->fs_servers);
hlist_del_rcu(&server->proc_link);
}
write_sequnlock(&net->fs_lock);
if (deleted) {
write_seqlock(&net->fs_addr_lock);
active = atomic_read(&server->active);
if (active == 0) {
trace_afs_server(server, atomic_read(&server->ref),
active, afs_server_trace_gc);
next = rcu_dereference_protected(
server->uuid_next, lockdep_is_held(&net->fs_lock.lock));
prev = server->uuid_prev;
if (!prev) {
/* The one at the front is in the tree */
if (!next) {
rb_erase(&server->uuid_rb, &net->fs_servers);
} else {
rb_replace_node_rcu(&server->uuid_rb,
&next->uuid_rb,
&net->fs_servers);
next->uuid_prev = NULL;
}
} else {
/* This server is not at the front */
rcu_assign_pointer(prev->uuid_next, next);
if (next)
next->uuid_prev = prev;
}
list_del(&server->probe_link);
hlist_del_rcu(&server->proc_link);
if (!hlist_unhashed(&server->addr4_link))
hlist_del_rcu(&server->addr4_link);
if (!hlist_unhashed(&server->addr6_link))
hlist_del_rcu(&server->addr6_link);
write_sequnlock(&net->fs_addr_lock);
afs_destroy_server(net, server);
}
write_sequnlock(&net->fs_lock);
if (active == 0)
afs_destroy_server(net, server);
}
}
@ -452,15 +546,14 @@ void afs_manage_servers(struct work_struct *work)
for (cursor = rb_first(&net->fs_servers); cursor; cursor = rb_next(cursor)) {
struct afs_server *server =
rb_entry(cursor, struct afs_server, uuid_rb);
int usage = atomic_read(&server->usage);
int active = atomic_read(&server->active);
_debug("manage %pU %u", &server->uuid, usage);
_debug("manage %pU %u", &server->uuid, active);
ASSERTCMP(usage, >=, 1);
ASSERTIFCMP(purging, usage, ==, 1);
ASSERTIFCMP(purging, active, ==, 0);
if (usage == 1) {
time64_t expire_at = server->put_time;
if (active == 0) {
time64_t expire_at = server->unuse_time;
if (!test_bit(AFS_SERVER_FL_VL_FAIL, &server->flags) &&
!test_bit(AFS_SERVER_FL_NOT_FOUND, &server->flags))
@ -525,26 +618,27 @@ void afs_purge_servers(struct afs_net *net)
/*
* Get an update for a server's address list.
*/
static noinline bool afs_update_server_record(struct afs_fs_cursor *fc, struct afs_server *server)
static noinline bool afs_update_server_record(struct afs_operation *op,
struct afs_server *server)
{
struct afs_addr_list *alist, *discard;
_enter("");
trace_afs_server(server, atomic_read(&server->usage), afs_server_trace_update);
trace_afs_server(server, atomic_read(&server->ref), atomic_read(&server->active),
afs_server_trace_update);
alist = afs_vl_lookup_addrs(fc->vnode->volume->cell, fc->key,
&server->uuid);
alist = afs_vl_lookup_addrs(op->volume->cell, op->key, &server->uuid);
if (IS_ERR(alist)) {
if ((PTR_ERR(alist) == -ERESTARTSYS ||
PTR_ERR(alist) == -EINTR) &&
!(fc->flags & AFS_FS_CURSOR_INTR) &&
(op->flags & AFS_OPERATION_UNINTR) &&
server->addresses) {
_leave(" = t [intr]");
return true;
}
fc->error = PTR_ERR(alist);
_leave(" = f [%d]", fc->error);
op->error = PTR_ERR(alist);
_leave(" = f [%d]", op->error);
return false;
}
@ -558,7 +652,6 @@ static noinline bool afs_update_server_record(struct afs_fs_cursor *fc, struct a
write_unlock(&server->fs_lock);
}
server->update_at = ktime_get_real_seconds() + afs_server_update_delay;
afs_put_addrlist(discard);
_leave(" = t");
return true;
@ -567,10 +660,8 @@ static noinline bool afs_update_server_record(struct afs_fs_cursor *fc, struct a
/*
* See if a server's address list needs updating.
*/
bool afs_check_server_record(struct afs_fs_cursor *fc, struct afs_server *server)
bool afs_check_server_record(struct afs_operation *op, struct afs_server *server)
{
time64_t now = ktime_get_real_seconds();
long diff;
bool success;
int ret, retries = 0;
@ -579,25 +670,29 @@ bool afs_check_server_record(struct afs_fs_cursor *fc, struct afs_server *server
ASSERT(server);
retry:
diff = READ_ONCE(server->update_at) - now;
if (diff > 0) {
_leave(" = t [not now %ld]", diff);
return true;
}
if (test_bit(AFS_SERVER_FL_UPDATING, &server->flags))
goto wait;
if (test_bit(AFS_SERVER_FL_NEEDS_UPDATE, &server->flags))
goto update;
_leave(" = t [good]");
return true;
update:
if (!test_and_set_bit_lock(AFS_SERVER_FL_UPDATING, &server->flags)) {
success = afs_update_server_record(fc, server);
clear_bit(AFS_SERVER_FL_NEEDS_UPDATE, &server->flags);
success = afs_update_server_record(op, server);
clear_bit_unlock(AFS_SERVER_FL_UPDATING, &server->flags);
wake_up_bit(&server->flags, AFS_SERVER_FL_UPDATING);
_leave(" = %d", success);
return success;
}
wait:
ret = wait_on_bit(&server->flags, AFS_SERVER_FL_UPDATING,
(fc->flags & AFS_FS_CURSOR_INTR) ?
TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
(op->flags & AFS_OPERATION_UNINTR) ?
TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
if (ret == -ERESTARTSYS) {
fc->error = ret;
op->error = ret;
_leave(" = f [intr]");
return false;
}

Просмотреть файл

@ -14,11 +14,9 @@ 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_put_cb_interest(net, slist->servers[i].cb_interest);
afs_put_server(net, slist->servers[i].server,
afs_server_trace_put_slist);
}
for (i = 0; i < slist->nr_servers; i++)
afs_unuse_server(net, slist->servers[i].server,
afs_server_trace_put_slist);
kfree(slist);
}
}
@ -46,12 +44,16 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,
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]);
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 ||
@ -123,31 +125,5 @@ changed:
}
}
/* Keep the old callback interest records where possible so that we
* maintain callback interception.
*/
i = 0;
j = 0;
while (i < old->nr_servers && j < new->nr_servers) {
if (new->servers[j].server == old->servers[i].server) {
struct afs_cb_interest *cbi = old->servers[i].cb_interest;
if (cbi) {
new->servers[j].cb_interest = cbi;
refcount_inc(&cbi->usage);
}
i++;
j++;
continue;
}
if (new->servers[j].server < old->servers[i].server) {
j++;
continue;
}
i++;
continue;
}
return true;
}

Просмотреть файл

@ -352,7 +352,9 @@ static int afs_validate_fc(struct fs_context *fc)
{
struct afs_fs_context *ctx = fc->fs_private;
struct afs_volume *volume;
struct afs_cell *cell;
struct key *key;
int ret;
if (!ctx->dyn_root) {
if (ctx->no_cell) {
@ -365,6 +367,7 @@ static int afs_validate_fc(struct fs_context *fc)
return -EDESTADDRREQ;
}
reget_key:
/* We try to do the mount securely. */
key = afs_request_key(ctx->cell);
if (IS_ERR(key))
@ -373,10 +376,26 @@ static int afs_validate_fc(struct fs_context *fc)
ctx->key = key;
if (ctx->volume) {
afs_put_volume(ctx->cell, ctx->volume);
afs_put_volume(ctx->net, ctx->volume,
afs_volume_trace_put_validate_fc);
ctx->volume = NULL;
}
if (test_bit(AFS_CELL_FL_CHECK_ALIAS, &ctx->cell->flags)) {
ret = afs_cell_detect_alias(ctx->cell, key);
if (ret < 0)
return ret;
if (ret == 1) {
_debug("switch to alias");
key_put(ctx->key);
ctx->key = NULL;
cell = afs_get_cell(ctx->cell->alias_of);
afs_put_cell(ctx->net, ctx->cell);
ctx->cell = cell;
goto reget_key;
}
}
volume = afs_create_volume(ctx);
if (IS_ERR(volume))
return PTR_ERR(volume);
@ -421,7 +440,6 @@ static int afs_set_super(struct super_block *sb, struct fs_context *fc)
static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
{
struct afs_super_info *as = AFS_FS_S(sb);
struct afs_iget_data iget_data;
struct inode *inode = NULL;
int ret;
@ -446,13 +464,7 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
} else {
sprintf(sb->s_id, "%llu", as->volume->vid);
afs_activate_volume(as->volume);
iget_data.fid.vid = as->volume->vid;
iget_data.fid.vnode = 1;
iget_data.fid.vnode_hi = 0;
iget_data.fid.unique = 1;
iget_data.cb_v_break = as->volume->cb_v_break;
iget_data.cb_s_break = 0;
inode = afs_iget(sb, ctx->key, &iget_data, NULL, NULL, NULL);
inode = afs_root_iget(sb, ctx->key);
}
if (IS_ERR(inode))
@ -473,6 +485,7 @@ static int afs_fill_super(struct super_block *sb, struct afs_fs_context *ctx)
goto error;
} else {
sb->s_d_op = &afs_fs_dentry_operations;
rcu_assign_pointer(as->volume->sb, sb);
}
_leave(" = 0");
@ -496,7 +509,8 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc)
as->dyn_root = true;
} else {
as->cell = afs_get_cell(ctx->cell);
as->volume = __afs_get_volume(ctx->volume);
as->volume = afs_get_volume(ctx->volume,
afs_volume_trace_get_alloc_sbi);
}
}
return as;
@ -505,8 +519,9 @@ static struct afs_super_info *afs_alloc_sbi(struct fs_context *fc)
static void afs_destroy_sbi(struct afs_super_info *as)
{
if (as) {
afs_put_volume(as->cell, as->volume);
afs_put_cell(afs_net(as->net_ns), as->cell);
struct afs_net *net = afs_net(as->net_ns);
afs_put_volume(net, as->volume, afs_volume_trace_put_destroy_sbi);
afs_put_cell(net, as->cell);
put_net(as->net_ns);
kfree(as);
}
@ -515,7 +530,6 @@ static void afs_destroy_sbi(struct afs_super_info *as)
static void afs_kill_super(struct super_block *sb)
{
struct afs_super_info *as = AFS_FS_S(sb);
struct afs_net *net = afs_net(as->net_ns);
if (as->dyn_root)
afs_dynroot_depopulate(sb);
@ -524,7 +538,7 @@ static void afs_kill_super(struct super_block *sb)
* deactivating the superblock.
*/
if (as->volume)
afs_clear_callback_interests(net, as->volume->servers);
rcu_assign_pointer(as->volume->sb, NULL);
kill_anon_super(sb);
if (as->volume)
afs_deactivate_volume(as->volume);
@ -592,7 +606,7 @@ static void afs_free_fc(struct fs_context *fc)
struct afs_fs_context *ctx = fc->fs_private;
afs_destroy_sbi(fc->s_fs_info);
afs_put_volume(ctx->cell, ctx->volume);
afs_put_volume(ctx->net, ctx->volume, afs_volume_trace_put_free_fc);
afs_put_cell(ctx->net, ctx->cell);
key_put(ctx->key);
kfree(ctx);
@ -674,7 +688,6 @@ static struct inode *afs_alloc_inode(struct super_block *sb)
vnode->volume = NULL;
vnode->lock_key = NULL;
vnode->permit_cache = NULL;
RCU_INIT_POINTER(vnode->cb_interest, NULL);
#ifdef CONFIG_AFS_FSCACHE
vnode->cache = NULL;
#endif
@ -704,22 +717,38 @@ static void afs_destroy_inode(struct inode *inode)
_debug("DESTROY INODE %p", inode);
ASSERTCMP(rcu_access_pointer(vnode->cb_interest), ==, NULL);
atomic_dec(&afs_count_active_inodes);
}
static void afs_get_volume_status_success(struct afs_operation *op)
{
struct afs_volume_status *vs = &op->volstatus.vs;
struct kstatfs *buf = op->volstatus.buf;
if (vs->max_quota == 0)
buf->f_blocks = vs->part_max_blocks;
else
buf->f_blocks = vs->max_quota;
if (buf->f_blocks > vs->blocks_in_use)
buf->f_bavail = buf->f_bfree =
buf->f_blocks - vs->blocks_in_use;
}
static const struct afs_operation_ops afs_get_volume_status_operation = {
.issue_afs_rpc = afs_fs_get_volume_status,
.issue_yfs_rpc = yfs_fs_get_volume_status,
.success = afs_get_volume_status_success,
};
/*
* return information about an AFS volume
*/
static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct afs_super_info *as = AFS_FS_S(dentry->d_sb);
struct afs_fs_cursor fc;
struct afs_volume_status vs;
struct afs_operation *op;
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
struct key *key;
int ret;
buf->f_type = dentry->d_sb->s_magic;
buf->f_bsize = AFS_BLOCK_SIZE;
@ -732,31 +761,13 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0;
}
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key))
return PTR_ERR(key);
op = afs_alloc_operation(NULL, as->volume);
if (IS_ERR(op))
return PTR_ERR(op);
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
fc.flags |= AFS_FS_CURSOR_NO_VSLEEP;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_get_volume_status(&fc, &vs);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
ret = afs_end_vnode_operation(&fc);
}
key_put(key);
if (ret == 0) {
if (vs.max_quota == 0)
buf->f_blocks = vs.part_max_blocks;
else
buf->f_blocks = vs.max_quota;
buf->f_bavail = buf->f_bfree = buf->f_blocks - vs.blocks_in_use;
}
return ret;
afs_op_set_vnode(op, 0, vnode);
op->nr_files = 1;
op->volstatus.buf = buf;
op->ops = &afs_get_volume_status_operation;
return afs_do_sync_operation(op);
}

382
fs/afs/vl_alias.c Normal file
Просмотреть файл

@ -0,0 +1,382 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* AFS cell alias detection
*
* Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/namei.h>
#include <keys/rxrpc-type.h>
#include "internal.h"
/*
* Sample a volume.
*/
static struct afs_volume *afs_sample_volume(struct afs_cell *cell, struct key *key,
const char *name, unsigned int namelen)
{
struct afs_volume *volume;
struct afs_fs_context fc = {
.type = 0, /* Explicitly leave it to the VLDB */
.volnamesz = namelen,
.volname = name,
.net = cell->net,
.cell = cell,
.key = key, /* This might need to be something */
};
volume = afs_create_volume(&fc);
_leave(" = %px", volume);
return volume;
}
/*
* Compare two addresses.
*/
static int afs_compare_addrs(const struct sockaddr_rxrpc *srx_a,
const struct sockaddr_rxrpc *srx_b)
{
short port_a, port_b;
int addr_a, addr_b, diff;
diff = (short)srx_a->transport_type - (short)srx_b->transport_type;
if (diff)
goto out;
switch (srx_a->transport_type) {
case AF_INET: {
const struct sockaddr_in *a = &srx_a->transport.sin;
const struct sockaddr_in *b = &srx_b->transport.sin;
addr_a = ntohl(a->sin_addr.s_addr);
addr_b = ntohl(b->sin_addr.s_addr);
diff = addr_a - addr_b;
if (diff == 0) {
port_a = ntohs(a->sin_port);
port_b = ntohs(b->sin_port);
diff = port_a - port_b;
}
break;
}
case AF_INET6: {
const struct sockaddr_in6 *a = &srx_a->transport.sin6;
const struct sockaddr_in6 *b = &srx_b->transport.sin6;
diff = memcmp(&a->sin6_addr, &b->sin6_addr, 16);
if (diff == 0) {
port_a = ntohs(a->sin6_port);
port_b = ntohs(b->sin6_port);
diff = port_a - port_b;
}
break;
}
default:
BUG();
}
out:
return diff;
}
/*
* Compare the address lists of a pair of fileservers.
*/
static int afs_compare_fs_alists(const struct afs_server *server_a,
const struct afs_server *server_b)
{
const struct afs_addr_list *la, *lb;
int a = 0, b = 0, addr_matches = 0;
la = rcu_dereference(server_a->addresses);
lb = rcu_dereference(server_b->addresses);
while (a < la->nr_addrs && b < lb->nr_addrs) {
const struct sockaddr_rxrpc *srx_a = &la->addrs[a];
const struct sockaddr_rxrpc *srx_b = &lb->addrs[b];
int diff = afs_compare_addrs(srx_a, srx_b);
if (diff < 0) {
a++;
} else if (diff > 0) {
b++;
} else {
addr_matches++;
a++;
b++;
}
}
return addr_matches;
}
/*
* Compare the fileserver lists of two volumes. The server lists are sorted in
* order of ascending UUID.
*/
static int afs_compare_volume_slists(const struct afs_volume *vol_a,
const struct afs_volume *vol_b)
{
const struct afs_server_list *la, *lb;
int i, a = 0, b = 0, uuid_matches = 0, addr_matches = 0;
la = rcu_dereference(vol_a->servers);
lb = rcu_dereference(vol_b->servers);
for (i = 0; i < AFS_MAXTYPES; i++)
if (la->vids[i] != lb->vids[i])
return 0;
while (a < la->nr_servers && b < lb->nr_servers) {
const struct afs_server *server_a = la->servers[a].server;
const struct afs_server *server_b = lb->servers[b].server;
int diff = memcmp(&server_a->uuid, &server_b->uuid, sizeof(uuid_t));
if (diff < 0) {
a++;
} else if (diff > 0) {
b++;
} else {
uuid_matches++;
addr_matches += afs_compare_fs_alists(server_a, server_b);
a++;
b++;
}
}
_leave(" = %d [um %d]", addr_matches, uuid_matches);
return addr_matches;
}
/*
* Compare root.cell volumes.
*/
static int afs_compare_cell_roots(struct afs_cell *cell)
{
struct afs_cell *p;
_enter("");
rcu_read_lock();
hlist_for_each_entry_rcu(p, &cell->net->proc_cells, proc_link) {
if (p == cell || p->alias_of)
continue;
if (!p->root_volume)
continue; /* Ignore cells that don't have a root.cell volume. */
if (afs_compare_volume_slists(cell->root_volume, p->root_volume) != 0)
goto is_alias;
}
rcu_read_unlock();
_leave(" = 0");
return 0;
is_alias:
rcu_read_unlock();
cell->alias_of = afs_get_cell(p);
return 1;
}
/*
* Query the new cell for a volume from a cell we're already using.
*/
static int afs_query_for_alias_one(struct afs_cell *cell, struct key *key,
struct afs_cell *p)
{
struct afs_volume *volume, *pvol = NULL;
int ret;
/* Arbitrarily pick a volume from the list. */
read_seqlock_excl(&p->volume_lock);
if (!RB_EMPTY_ROOT(&p->volumes))
pvol = afs_get_volume(rb_entry(p->volumes.rb_node,
struct afs_volume, cell_node),
afs_volume_trace_get_query_alias);
read_sequnlock_excl(&p->volume_lock);
if (!pvol)
return 0;
_enter("%s:%s", cell->name, pvol->name);
/* And see if it's in the new cell. */
volume = afs_sample_volume(cell, key, pvol->name, pvol->name_len);
if (IS_ERR(volume)) {
afs_put_volume(cell->net, pvol, afs_volume_trace_put_query_alias);
if (PTR_ERR(volume) != -ENOMEDIUM)
return PTR_ERR(volume);
/* That volume is not in the new cell, so not an alias */
return 0;
}
/* The new cell has a like-named volume also - compare volume ID,
* server and address lists.
*/
ret = 0;
if (pvol->vid == volume->vid) {
rcu_read_lock();
if (afs_compare_volume_slists(volume, pvol))
ret = 1;
rcu_read_unlock();
}
afs_put_volume(cell->net, volume, afs_volume_trace_put_query_alias);
afs_put_volume(cell->net, pvol, afs_volume_trace_put_query_alias);
return ret;
}
/*
* Query the new cell for volumes we know exist in cells we're already using.
*/
static int afs_query_for_alias(struct afs_cell *cell, struct key *key)
{
struct afs_cell *p;
_enter("%s", cell->name);
if (mutex_lock_interruptible(&cell->net->proc_cells_lock) < 0)
return -ERESTARTSYS;
hlist_for_each_entry(p, &cell->net->proc_cells, proc_link) {
if (p == cell || p->alias_of)
continue;
if (RB_EMPTY_ROOT(&p->volumes))
continue;
if (p->root_volume)
continue; /* Ignore cells that have a root.cell volume. */
afs_get_cell(p);
mutex_unlock(&cell->net->proc_cells_lock);
if (afs_query_for_alias_one(cell, key, p) != 0)
goto is_alias;
if (mutex_lock_interruptible(&cell->net->proc_cells_lock) < 0) {
afs_put_cell(cell->net, p);
return -ERESTARTSYS;
}
afs_put_cell(cell->net, p);
}
mutex_unlock(&cell->net->proc_cells_lock);
_leave(" = 0");
return 0;
is_alias:
cell->alias_of = p; /* Transfer our ref */
return 1;
}
/*
* Look up a VLDB record for a volume.
*/
static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key)
{
struct afs_vl_cursor vc;
char *cell_name = ERR_PTR(-EDESTADDRREQ);
bool skipped = false, not_skipped = false;
int ret;
if (!afs_begin_vlserver_operation(&vc, cell, key))
return ERR_PTR(-ERESTARTSYS);
while (afs_select_vlserver(&vc)) {
if (!test_bit(AFS_VLSERVER_FL_IS_YFS, &vc.server->flags)) {
vc.ac.error = -EOPNOTSUPP;
skipped = true;
continue;
}
not_skipped = true;
cell_name = afs_yfsvl_get_cell_name(&vc);
}
ret = afs_end_vlserver_operation(&vc);
if (skipped && !not_skipped)
ret = -EOPNOTSUPP;
return ret < 0 ? ERR_PTR(ret) : cell_name;
}
static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
{
struct afs_cell *master;
char *cell_name;
cell_name = afs_vl_get_cell_name(cell, key);
if (IS_ERR(cell_name))
return PTR_ERR(cell_name);
if (strcmp(cell_name, cell->name) == 0) {
kfree(cell_name);
return 0;
}
master = afs_lookup_cell(cell->net, cell_name, strlen(cell_name),
NULL, false);
kfree(cell_name);
if (IS_ERR(master))
return PTR_ERR(master);
cell->alias_of = master; /* Transfer our ref */
return 1;
}
static int afs_do_cell_detect_alias(struct afs_cell *cell, struct key *key)
{
struct afs_volume *root_volume;
int ret;
_enter("%s", cell->name);
ret = yfs_check_canonical_cell_name(cell, key);
if (ret != -EOPNOTSUPP)
return ret;
/* Try and get the root.cell volume for comparison with other cells */
root_volume = afs_sample_volume(cell, key, "root.cell", 9);
if (!IS_ERR(root_volume)) {
cell->root_volume = root_volume;
return afs_compare_cell_roots(cell);
}
if (PTR_ERR(root_volume) != -ENOMEDIUM)
return PTR_ERR(root_volume);
/* Okay, this cell doesn't have an root.cell volume. We need to
* locate some other random volume and use that to check.
*/
return afs_query_for_alias(cell, key);
}
/*
* Check to see if a new cell is an alias of a cell we already have. At this
* point we have the cell's volume server list.
*
* Returns 0 if we didn't detect an alias, 1 if we found an alias and an error
* if we had problems gathering the data required. In the case the we did
* detect an alias, cell->alias_of is set to point to the assumed master.
*/
int afs_cell_detect_alias(struct afs_cell *cell, struct key *key)
{
struct afs_net *net = cell->net;
int ret;
if (mutex_lock_interruptible(&net->cells_alias_lock) < 0)
return -ERESTARTSYS;
if (test_bit(AFS_CELL_FL_CHECK_ALIAS, &cell->flags)) {
ret = afs_do_cell_detect_alias(cell, key);
if (ret >= 0)
clear_bit_unlock(AFS_CELL_FL_CHECK_ALIAS, &cell->flags);
} else {
ret = cell->alias_of ? 1 : 0;
}
mutex_unlock(&net->cells_alias_lock);
if (ret == 1)
pr_notice("kAFS: Cell %s is an alias of %s\n",
cell->name, cell->alias_of->name);
return ret;
}

Просмотреть файл

@ -151,6 +151,10 @@ bool afs_select_vlserver(struct afs_vl_cursor *vc)
vc->error = error;
vc->flags |= AFS_VL_CURSOR_RETRY;
goto next_server;
case -EOPNOTSUPP:
_debug("notsupp");
goto next_server;
}
restart_from_beginning:

Просмотреть файл

@ -82,6 +82,7 @@ static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call)
for (j = 0; j < 6; j++)
uuid->node[j] = (u8)ntohl(xdr->node[j]);
entry->addr_version[n] = ntohl(uvldb->serverUnique[i]);
entry->nr_servers++;
}
@ -447,8 +448,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
call->count2 = ntohl(*bp); /* Type or next count */
if (call->count > YFS_MAXENDPOINTS)
return afs_protocol_error(call, -EBADMSG,
afs_eproto_yvl_fsendpt_num);
return afs_protocol_error(call, afs_eproto_yvl_fsendpt_num);
alist = afs_alloc_addrlist(call->count, FS_SERVICE, AFS_FS_PORT);
if (!alist)
@ -468,8 +468,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
size = sizeof(__be32) * (1 + 4 + 1);
break;
default:
return afs_protocol_error(call, -EBADMSG,
afs_eproto_yvl_fsendpt_type);
return afs_protocol_error(call, afs_eproto_yvl_fsendpt_type);
}
size += sizeof(__be32);
@ -487,21 +486,20 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
switch (call->count2) {
case YFS_ENDPOINT_IPV4:
if (ntohl(bp[0]) != sizeof(__be32) * 2)
return afs_protocol_error(call, -EBADMSG,
afs_eproto_yvl_fsendpt4_len);
return afs_protocol_error(
call, afs_eproto_yvl_fsendpt4_len);
afs_merge_fs_addr4(alist, bp[1], ntohl(bp[2]));
bp += 3;
break;
case YFS_ENDPOINT_IPV6:
if (ntohl(bp[0]) != sizeof(__be32) * 5)
return afs_protocol_error(call, -EBADMSG,
afs_eproto_yvl_fsendpt6_len);
return afs_protocol_error(
call, afs_eproto_yvl_fsendpt6_len);
afs_merge_fs_addr6(alist, bp + 1, ntohl(bp[5]));
bp += 6;
break;
default:
return afs_protocol_error(call, -EBADMSG,
afs_eproto_yvl_fsendpt_type);
return afs_protocol_error(call, afs_eproto_yvl_fsendpt_type);
}
/* Got either the type of the next entry or the count of
@ -519,8 +517,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
if (!call->count)
goto end;
if (call->count > YFS_MAXENDPOINTS)
return afs_protocol_error(call, -EBADMSG,
afs_eproto_yvl_vlendpt_type);
return afs_protocol_error(call, afs_eproto_yvl_vlendpt_type);
afs_extract_to_buf(call, 1 * sizeof(__be32));
call->unmarshall = 3;
@ -547,8 +544,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
size = sizeof(__be32) * (1 + 4 + 1);
break;
default:
return afs_protocol_error(call, -EBADMSG,
afs_eproto_yvl_vlendpt_type);
return afs_protocol_error(call, afs_eproto_yvl_vlendpt_type);
}
if (call->count > 1)
@ -566,19 +562,18 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
switch (call->count2) {
case YFS_ENDPOINT_IPV4:
if (ntohl(bp[0]) != sizeof(__be32) * 2)
return afs_protocol_error(call, -EBADMSG,
afs_eproto_yvl_vlendpt4_len);
return afs_protocol_error(
call, afs_eproto_yvl_vlendpt4_len);
bp += 3;
break;
case YFS_ENDPOINT_IPV6:
if (ntohl(bp[0]) != sizeof(__be32) * 5)
return afs_protocol_error(call, -EBADMSG,
afs_eproto_yvl_vlendpt6_len);
return afs_protocol_error(
call, afs_eproto_yvl_vlendpt6_len);
bp += 6;
break;
default:
return afs_protocol_error(call, -EBADMSG,
afs_eproto_yvl_vlendpt_type);
return afs_protocol_error(call, afs_eproto_yvl_vlendpt_type);
}
/* Got either the type of the next entry or the count of
@ -650,3 +645,114 @@ struct afs_addr_list *afs_yfsvl_get_endpoints(struct afs_vl_cursor *vc,
afs_make_call(&vc->ac, call, GFP_KERNEL);
return (struct afs_addr_list *)afs_wait_for_call_to_complete(call, &vc->ac);
}
/*
* Deliver reply data to a YFSVL.GetCellName operation.
*/
static int afs_deliver_yfsvl_get_cell_name(struct afs_call *call)
{
char *cell_name;
u32 namesz, paddedsz;
int ret;
_enter("{%u,%zu/%u}",
call->unmarshall, iov_iter_count(call->iter), call->count);
switch (call->unmarshall) {
case 0:
afs_extract_to_tmp(call);
call->unmarshall++;
/* Fall through - and extract the cell name length */
case 1:
ret = afs_extract_data(call, true);
if (ret < 0)
return ret;
namesz = ntohl(call->tmp);
if (namesz > AFS_MAXCELLNAME)
return afs_protocol_error(call, afs_eproto_cellname_len);
paddedsz = (namesz + 3) & ~3;
call->count = namesz;
call->count2 = paddedsz - namesz;
cell_name = kmalloc(namesz + 1, GFP_KERNEL);
if (!cell_name)
return -ENOMEM;
cell_name[namesz] = 0;
call->ret_str = cell_name;
afs_extract_begin(call, cell_name, namesz);
call->unmarshall++;
/* Fall through - and extract cell name */
case 2:
ret = afs_extract_data(call, true);
if (ret < 0)
return ret;
afs_extract_discard(call, call->count2);
call->unmarshall++;
/* Fall through - and extract padding */
case 3:
ret = afs_extract_data(call, false);
if (ret < 0)
return ret;
call->unmarshall++;
break;
}
_leave(" = 0 [done]");
return 0;
}
static void afs_destroy_yfsvl_get_cell_name(struct afs_call *call)
{
kfree(call->ret_str);
afs_flat_call_destructor(call);
}
/*
* VL.GetCapabilities operation type
*/
static const struct afs_call_type afs_YFSVLGetCellName = {
.name = "YFSVL.GetCellName",
.op = afs_YFSVL_GetCellName,
.deliver = afs_deliver_yfsvl_get_cell_name,
.destructor = afs_destroy_yfsvl_get_cell_name,
};
/*
* Probe a volume server for the capabilities that it supports. This can
* return up to 196 words.
*
* We use this to probe for service upgrade to determine what the server at the
* other end supports.
*/
char *afs_yfsvl_get_cell_name(struct afs_vl_cursor *vc)
{
struct afs_call *call;
struct afs_net *net = vc->cell->net;
__be32 *bp;
_enter("");
call = afs_alloc_flat_call(net, &afs_YFSVLGetCellName, 1 * 4, 0);
if (!call)
return ERR_PTR(-ENOMEM);
call->key = vc->key;
call->ret_str = NULL;
call->max_lifespan = AFS_VL_MAX_LIFESPAN;
/* marshall the parameters */
bp = call->request;
*bp++ = htonl(YVLGETCELLNAME);
/* Can't take a ref on server */
trace_afs_make_vl_call(call);
afs_make_call(&vc->ac, call, GFP_KERNEL);
return (char *)afs_wait_for_call_to_complete(call, &vc->ac);
}

Просмотреть файл

@ -12,6 +12,56 @@
unsigned __read_mostly afs_volume_gc_delay = 10;
unsigned __read_mostly afs_volume_record_life = 60 * 60;
/*
* Insert a volume into a cell. If there's an existing volume record, that is
* returned instead with a ref held.
*/
static struct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell,
struct afs_volume *volume)
{
struct afs_volume *p;
struct rb_node *parent = NULL, **pp;
write_seqlock(&cell->volume_lock);
pp = &cell->volumes.rb_node;
while (*pp) {
parent = *pp;
p = rb_entry(parent, struct afs_volume, cell_node);
if (p->vid < volume->vid) {
pp = &(*pp)->rb_left;
} else if (p->vid > volume->vid) {
pp = &(*pp)->rb_right;
} else {
volume = afs_get_volume(p, afs_volume_trace_get_cell_insert);
goto found;
}
}
rb_link_node_rcu(&volume->cell_node, parent, pp);
rb_insert_color(&volume->cell_node, &cell->volumes);
hlist_add_head_rcu(&volume->proc_link, &cell->proc_volumes);
found:
write_sequnlock(&cell->volume_lock);
return volume;
}
static void afs_remove_volume_from_cell(struct afs_volume *volume)
{
struct afs_cell *cell = volume->cell;
if (!hlist_unhashed(&volume->proc_link)) {
trace_afs_volume(volume->vid, atomic_read(&volume->usage),
afs_volume_trace_remove);
write_seqlock(&cell->volume_lock);
hlist_del_rcu(&volume->proc_link);
rb_erase(&volume->cell_node, &cell->volumes);
write_sequnlock(&cell->volume_lock);
}
}
/*
* Allocate a volume record and load it up from a vldb record.
*/
@ -39,7 +89,7 @@ static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params,
volume->name_len = vldb->name_len;
atomic_set(&volume->usage, 1);
INIT_LIST_HEAD(&volume->proc_link);
INIT_HLIST_NODE(&volume->proc_link);
rwlock_init(&volume->servers_lock);
rwlock_init(&volume->cb_v_break_lock);
memcpy(volume->name, vldb->name, vldb->name_len + 1);
@ -51,7 +101,8 @@ static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params,
}
refcount_set(&slist->usage, 1);
volume->servers = slist;
rcu_assign_pointer(volume->servers, slist);
trace_afs_volume(volume->vid, 1, afs_volume_trace_alloc);
return volume;
error_1:
@ -61,6 +112,25 @@ error_0:
return ERR_PTR(ret);
}
/*
* Look up or allocate a volume record.
*/
static struct afs_volume *afs_lookup_volume(struct afs_fs_context *params,
struct afs_vldb_entry *vldb,
unsigned long type_mask)
{
struct afs_volume *candidate, *volume;
candidate = afs_alloc_volume(params, vldb, type_mask);
if (IS_ERR(candidate))
return candidate;
volume = afs_insert_volume_into_cell(params->cell, candidate);
if (volume != candidate)
afs_put_volume(params->net, candidate, afs_volume_trace_put_cell_dup);
return volume;
}
/*
* Look up a VLDB record for a volume.
*/
@ -138,7 +208,7 @@ struct afs_volume *afs_create_volume(struct afs_fs_context *params)
}
type_mask = 1UL << params->type;
volume = afs_alloc_volume(params, vldb, type_mask);
volume = afs_lookup_volume(params, vldb, type_mask);
error:
kfree(vldb);
@ -156,23 +226,42 @@ static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume)
ASSERTCMP(volume->cache, ==, NULL);
#endif
afs_put_serverlist(net, volume->servers);
afs_remove_volume_from_cell(volume);
afs_put_serverlist(net, rcu_access_pointer(volume->servers));
afs_put_cell(net, volume->cell);
kfree(volume);
trace_afs_volume(volume->vid, atomic_read(&volume->usage),
afs_volume_trace_free);
kfree_rcu(volume, rcu);
_leave(" [destroyed]");
}
/*
* Drop a reference on a volume record.
* Get a reference on a volume record.
*/
void afs_put_volume(struct afs_cell *cell, struct afs_volume *volume)
struct afs_volume *afs_get_volume(struct afs_volume *volume,
enum afs_volume_trace reason)
{
if (volume) {
_enter("%s", volume->name);
int u = atomic_inc_return(&volume->usage);
trace_afs_volume(volume->vid, u, reason);
}
return volume;
}
if (atomic_dec_and_test(&volume->usage))
afs_destroy_volume(cell->net, volume);
/*
* Drop a reference on a volume record.
*/
void afs_put_volume(struct afs_net *net, struct afs_volume *volume,
enum afs_volume_trace reason)
{
if (volume) {
afs_volid_t vid = volume->vid;
int u = atomic_dec_return(&volume->usage);
trace_afs_volume(vid, u, reason);
if (u == 0)
afs_destroy_volume(net, volume);
}
}
@ -188,10 +277,6 @@ void afs_activate_volume(struct afs_volume *volume)
NULL, 0,
volume, 0, true);
#endif
write_lock(&volume->cell->proc_lock);
list_add_tail(&volume->proc_link, &volume->cell->proc_volumes);
write_unlock(&volume->cell->proc_lock);
}
/*
@ -201,10 +286,6 @@ void afs_deactivate_volume(struct afs_volume *volume)
{
_enter("%s", volume->name);
write_lock(&volume->cell->proc_lock);
list_del_init(&volume->proc_link);
write_unlock(&volume->cell->proc_lock);
#ifdef CONFIG_AFS_FSCACHE
fscache_relinquish_cookie(volume->cache, NULL,
test_bit(AFS_VOLUME_DELETED, &volume->flags));
@ -256,17 +337,17 @@ static int afs_update_volume_status(struct afs_volume *volume, struct key *key)
write_lock(&volume->servers_lock);
discard = new;
old = volume->servers;
old = rcu_dereference_protected(volume->servers,
lockdep_is_held(&volume->servers_lock));
if (afs_annotate_server_list(new, old)) {
new->seq = volume->servers_seq + 1;
volume->servers = new;
rcu_assign_pointer(volume->servers, new);
smp_wmb();
volume->servers_seq++;
discard = old;
}
volume->update_at = ktime_get_real_seconds() + afs_volume_record_life;
clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
write_unlock(&volume->servers_lock);
ret = 0;
@ -281,25 +362,27 @@ error:
/*
* Make sure the volume record is up to date.
*/
int afs_check_volume_status(struct afs_volume *volume, struct afs_fs_cursor *fc)
int afs_check_volume_status(struct afs_volume *volume, struct afs_operation *op)
{
time64_t now = ktime_get_real_seconds();
int ret, retries = 0;
_enter("");
if (volume->update_at <= now)
set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
retry:
if (!test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags) &&
!test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
_leave(" = 0");
return 0;
}
if (test_bit(AFS_VOLUME_WAIT, &volume->flags))
goto wait;
if (volume->update_at <= ktime_get_real_seconds() ||
test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags))
goto update;
_leave(" = 0");
return 0;
update:
if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) {
ret = afs_update_volume_status(volume, fc->key);
clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
ret = afs_update_volume_status(volume, op->key);
if (ret < 0)
set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags);
clear_bit_unlock(AFS_VOLUME_UPDATING, &volume->flags);
wake_up_bit(&volume->flags, AFS_VOLUME_WAIT);
@ -307,14 +390,15 @@ retry:
return ret;
}
wait:
if (!test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
_leave(" = 0 [no wait]");
return 0;
}
ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT,
(fc->flags & AFS_FS_CURSOR_INTR) ?
TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
(op->flags & AFS_OPERATION_UNINTR) ?
TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
if (ret == -ERESTARTSYS) {
_leave(" = %d", ret);
return ret;

Просмотреть файл

@ -348,6 +348,67 @@ static void afs_pages_written_back(struct afs_vnode *vnode,
_leave("");
}
/*
* Find a key to use for the writeback. We cached the keys used to author the
* writes on the vnode. *_wbk will contain the last writeback key used or NULL
* and we need to start from there if it's set.
*/
static int afs_get_writeback_key(struct afs_vnode *vnode,
struct afs_wb_key **_wbk)
{
struct afs_wb_key *wbk = NULL;
struct list_head *p;
int ret = -ENOKEY, ret2;
spin_lock(&vnode->wb_lock);
if (*_wbk)
p = (*_wbk)->vnode_link.next;
else
p = vnode->wb_keys.next;
while (p != &vnode->wb_keys) {
wbk = list_entry(p, struct afs_wb_key, vnode_link);
_debug("wbk %u", key_serial(wbk->key));
ret2 = key_validate(wbk->key);
if (ret2 == 0) {
refcount_inc(&wbk->usage);
_debug("USE WB KEY %u", key_serial(wbk->key));
break;
}
wbk = NULL;
if (ret == -ENOKEY)
ret = ret2;
p = p->next;
}
spin_unlock(&vnode->wb_lock);
if (*_wbk)
afs_put_wb_key(*_wbk);
*_wbk = wbk;
return 0;
}
static void afs_store_data_success(struct afs_operation *op)
{
struct afs_vnode *vnode = op->file[0].vnode;
afs_vnode_commit_status(op, &op->file[0]);
if (op->error == 0) {
afs_pages_written_back(vnode, op->store.first, op->store.last);
afs_stat_v(vnode, n_stores);
atomic_long_add((op->store.last * PAGE_SIZE + op->store.last_to) -
(op->store.first * PAGE_SIZE + op->store.first_offset),
&afs_v2net(vnode)->n_store_bytes);
}
}
static const struct afs_operation_ops afs_store_data_operation = {
.issue_afs_rpc = afs_fs_store_data,
.issue_yfs_rpc = yfs_fs_store_data,
.success = afs_store_data_success,
};
/*
* write to a file
*/
@ -356,11 +417,9 @@ static int afs_store_data(struct address_space *mapping,
unsigned offset, unsigned to)
{
struct afs_vnode *vnode = AFS_FS_I(mapping->host);
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_operation *op;
struct afs_wb_key *wbk = NULL;
struct list_head *p;
int ret = -ENOKEY, ret2;
int ret;
_enter("%s{%llx:%llu.%u},%lx,%lx,%x,%x",
vnode->volume->name,
@ -369,62 +428,32 @@ static int afs_store_data(struct address_space *mapping,
vnode->fid.unique,
first, last, offset, to);
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
ret = afs_get_writeback_key(vnode, &wbk);
if (ret) {
_leave(" = %d [no keys]", ret);
return ret;
}
op = afs_alloc_operation(wbk->key, vnode->volume);
if (IS_ERR(op)) {
afs_put_wb_key(wbk);
return -ENOMEM;
}
spin_lock(&vnode->wb_lock);
p = vnode->wb_keys.next;
afs_op_set_vnode(op, 0, vnode);
op->file[0].dv_delta = 1;
op->store.mapping = mapping;
op->store.first = first;
op->store.last = last;
op->store.first_offset = offset;
op->store.last_to = to;
op->ops = &afs_store_data_operation;
/* Iterate through the list looking for a valid key to use. */
try_next_key:
while (p != &vnode->wb_keys) {
wbk = list_entry(p, struct afs_wb_key, vnode_link);
_debug("wbk %u", key_serial(wbk->key));
ret2 = key_validate(wbk->key);
if (ret2 == 0)
goto found_key;
if (ret == -ENOKEY)
ret = ret2;
p = p->next;
}
afs_begin_vnode_operation(op);
afs_wait_for_operation(op);
spin_unlock(&vnode->wb_lock);
afs_put_wb_key(wbk);
kfree(scb);
_leave(" = %d [no keys]", ret);
return ret;
found_key:
refcount_inc(&wbk->usage);
spin_unlock(&vnode->wb_lock);
_debug("USE WB KEY %u", key_serial(wbk->key));
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, wbk->key, false)) {
afs_dataversion_t data_version = vnode->status.data_version + 1;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_store_data(&fc, mapping, first, last, offset, to, scb);
}
afs_check_for_remote_deletion(&fc, vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
if (fc.ac.error == 0)
afs_pages_written_back(vnode, first, last);
ret = afs_end_vnode_operation(&fc);
}
switch (ret) {
case 0:
afs_stat_v(vnode, n_stores);
atomic_long_add((last * PAGE_SIZE + to) -
(first * PAGE_SIZE + offset),
&afs_v2net(vnode)->n_store_bytes);
break;
switch (op->error) {
case -EACCES:
case -EPERM:
case -ENOKEY:
@ -432,16 +461,19 @@ found_key:
case -EKEYREJECTED:
case -EKEYREVOKED:
_debug("next");
spin_lock(&vnode->wb_lock);
p = wbk->vnode_link.next;
afs_put_wb_key(wbk);
goto try_next_key;
ret = afs_get_writeback_key(vnode, &wbk);
if (ret == 0) {
key_put(op->key);
op->key = key_get(wbk->key);
goto try_next_key;
}
break;
}
afs_put_wb_key(wbk);
kfree(scb);
_leave(" = %d", ret);
return ret;
_leave(" = %d", op->error);
return afs_put_operation(op);
}
/*

Просмотреть файл

@ -34,6 +34,25 @@ ssize_t afs_listxattr(struct dentry *dentry, char *buffer, size_t size)
return sizeof(afs_xattr_list);
}
/*
* Deal with the result of a successful fetch ACL operation.
*/
static void afs_acl_success(struct afs_operation *op)
{
afs_vnode_commit_status(op, &op->file[0]);
}
static void afs_acl_put(struct afs_operation *op)
{
kfree(op->acl);
}
static const struct afs_operation_ops afs_fetch_acl_operation = {
.issue_afs_rpc = afs_fs_fetch_acl,
.success = afs_acl_success,
.put = afs_acl_put,
};
/*
* Get a file's ACL.
*/
@ -42,37 +61,23 @@ static int afs_xattr_get_acl(const struct xattr_handler *handler,
struct inode *inode, const char *name,
void *buffer, size_t size)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_operation *op;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_acl *acl = NULL;
struct key *key;
int ret = -ENOMEM;
int ret;
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
goto error;
op = afs_alloc_operation(NULL, vnode->volume);
if (IS_ERR(op))
return -ENOMEM;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error_scb;
}
afs_op_set_vnode(op, 0, vnode);
op->ops = &afs_fetch_acl_operation;
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
acl = afs_fs_fetch_acl(&fc, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
afs_begin_vnode_operation(op);
afs_wait_for_operation(op);
acl = op->acl;
op->acl = NULL;
ret = afs_put_operation(op);
if (ret == 0) {
ret = acl->size;
@ -80,18 +85,37 @@ static int afs_xattr_get_acl(const struct xattr_handler *handler,
if (acl->size <= size)
memcpy(buffer, acl->data, acl->size);
else
ret = -ERANGE;
op->error = -ERANGE;
}
kfree(acl);
}
key_put(key);
error_scb:
kfree(scb);
error:
kfree(acl);
return ret;
}
static bool afs_make_acl(struct afs_operation *op,
const void *buffer, size_t size)
{
struct afs_acl *acl;
acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
if (!acl) {
afs_op_nomem(op);
return false;
}
acl->size = size;
memcpy(acl->data, buffer, size);
op->acl = acl;
return true;
}
static const struct afs_operation_ops afs_store_acl_operation = {
.issue_afs_rpc = afs_fs_store_acl,
.success = afs_acl_success,
.put = afs_acl_put,
};
/*
* Set a file's AFS3 ACL.
*/
@ -100,55 +124,22 @@ static int afs_xattr_set_acl(const struct xattr_handler *handler,
struct inode *inode, const char *name,
const void *buffer, size_t size, int flags)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_operation *op;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_acl *acl = NULL;
struct key *key;
int ret = -ENOMEM;
if (flags == XATTR_CREATE)
return -EINVAL;
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
goto error;
op = afs_alloc_operation(NULL, vnode->volume);
if (IS_ERR(op))
return -ENOMEM;
acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
if (!acl)
goto error_scb;
afs_op_set_vnode(op, 0, vnode);
if (!afs_make_acl(op, buffer, size))
return afs_put_operation(op);
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error_acl;
}
acl->size = size;
memcpy(acl->data, buffer, size);
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
afs_fs_store_acl(&fc, acl, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
key_put(key);
error_acl:
kfree(acl);
error_scb:
kfree(scb);
error:
return ret;
op->ops = &afs_store_acl_operation;
return afs_do_sync_operation(op);
}
static const struct xattr_handler afs_xattr_afs_acl_handler = {
@ -157,6 +148,17 @@ static const struct xattr_handler afs_xattr_afs_acl_handler = {
.set = afs_xattr_set_acl,
};
static void yfs_acl_put(struct afs_operation *op)
{
yfs_free_opaque_acl(op->yacl);
}
static const struct afs_operation_ops yfs_fetch_opaque_acl_operation = {
.issue_yfs_rpc = yfs_fs_fetch_opaque_acl,
.success = afs_acl_success,
/* Don't free op->yacl in .put here */
};
/*
* Get a file's YFS ACL.
*/
@ -165,11 +167,9 @@ static int afs_xattr_get_yfs(const struct xattr_handler *handler,
struct inode *inode, const char *name,
void *buffer, size_t size)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_operation *op;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct yfs_acl *yacl = NULL;
struct key *key;
char buf[16], *data;
int which = 0, dsize, ret = -ENOMEM;
@ -193,75 +193,62 @@ static int afs_xattr_get_yfs(const struct xattr_handler *handler,
else if (which == 3)
yacl->flags |= YFS_ACL_WANT_VOL_ACL;
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
op = afs_alloc_operation(NULL, vnode->volume);
if (IS_ERR(op))
goto error_yacl;
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error_scb;
}
afs_op_set_vnode(op, 0, vnode);
op->yacl = yacl;
op->ops = &yfs_fetch_opaque_acl_operation;
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
afs_begin_vnode_operation(op);
afs_wait_for_operation(op);
ret = afs_put_operation(op);
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
yfs_fs_fetch_opaque_acl(&fc, yacl, scb);
if (ret == 0) {
switch (which) {
case 0:
data = yacl->acl->data;
dsize = yacl->acl->size;
break;
case 1:
data = buf;
dsize = scnprintf(buf, sizeof(buf), "%u", yacl->inherit_flag);
break;
case 2:
data = buf;
dsize = scnprintf(buf, sizeof(buf), "%u", yacl->num_cleaned);
break;
case 3:
data = yacl->vol_acl->data;
dsize = yacl->vol_acl->size;
break;
default:
ret = -EOPNOTSUPP;
goto error_yacl;
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
if (ret < 0)
goto error_key;
switch (which) {
case 0:
data = yacl->acl->data;
dsize = yacl->acl->size;
break;
case 1:
data = buf;
dsize = scnprintf(buf, sizeof(buf), "%u", yacl->inherit_flag);
break;
case 2:
data = buf;
dsize = scnprintf(buf, sizeof(buf), "%u", yacl->num_cleaned);
break;
case 3:
data = yacl->vol_acl->data;
dsize = yacl->vol_acl->size;
break;
default:
ret = -EOPNOTSUPP;
goto error_key;
}
ret = dsize;
if (size > 0) {
if (dsize > size) {
ret = -ERANGE;
goto error_key;
ret = dsize;
if (size > 0) {
if (dsize <= size)
memcpy(buffer, data, dsize);
else
ret = -ERANGE;
}
memcpy(buffer, data, dsize);
}
error_key:
key_put(key);
error_scb:
kfree(scb);
error_yacl:
yfs_free_opaque_acl(yacl);
error:
return ret;
}
static const struct afs_operation_ops yfs_store_opaque_acl2_operation = {
.issue_yfs_rpc = yfs_fs_store_opaque_acl2,
.success = afs_acl_success,
.put = yfs_acl_put,
};
/*
* Set a file's YFS ACL.
*/
@ -270,56 +257,23 @@ static int afs_xattr_set_yfs(const struct xattr_handler *handler,
struct inode *inode, const char *name,
const void *buffer, size_t size, int flags)
{
struct afs_fs_cursor fc;
struct afs_status_cb *scb;
struct afs_operation *op;
struct afs_vnode *vnode = AFS_FS_I(inode);
struct afs_acl *acl = NULL;
struct key *key;
int ret = -ENOMEM;
if (flags == XATTR_CREATE ||
strcmp(name, "acl") != 0)
return -EINVAL;
scb = kzalloc(sizeof(struct afs_status_cb), GFP_NOFS);
if (!scb)
goto error;
op = afs_alloc_operation(NULL, vnode->volume);
if (IS_ERR(op))
return -ENOMEM;
acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
if (!acl)
goto error_scb;
afs_op_set_vnode(op, 0, vnode);
if (!afs_make_acl(op, buffer, size))
return afs_put_operation(op);
acl->size = size;
memcpy(acl->data, buffer, size);
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
ret = PTR_ERR(key);
goto error_acl;
}
ret = -ERESTARTSYS;
if (afs_begin_vnode_operation(&fc, vnode, key, true)) {
afs_dataversion_t data_version = vnode->status.data_version;
while (afs_select_fileserver(&fc)) {
fc.cb_break = afs_calc_vnode_cb_break(vnode);
yfs_fs_store_opaque_acl2(&fc, acl, scb);
}
afs_check_for_remote_deletion(&fc, fc.vnode);
afs_vnode_commit_status(&fc, vnode, fc.cb_break,
&data_version, scb);
ret = afs_end_vnode_operation(&fc);
}
error_acl:
kfree(acl);
key_put(key);
error_scb:
kfree(scb);
error:
return ret;
op->ops = &yfs_store_opaque_acl2_operation;
return afs_do_sync_operation(op);
}
static const struct xattr_handler afs_xattr_yfs_handler = {

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -4865,21 +4865,22 @@ static int ext4_inode_blocks_set(handle_t *handle,
return 0;
}
struct other_inode {
unsigned long orig_ino;
struct ext4_inode *raw_inode;
};
static int other_inode_match(struct inode * inode, unsigned long ino,
void *data)
static void __ext4_update_other_inode_time(struct super_block *sb,
unsigned long orig_ino,
unsigned long ino,
struct ext4_inode *raw_inode)
{
struct other_inode *oi = (struct other_inode *) data;
struct inode *inode;
if ((inode->i_ino != ino) ||
(inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW |
inode = find_inode_by_ino_rcu(sb, ino);
if (!inode)
return;
if ((inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW |
I_DIRTY_INODE)) ||
((inode->i_state & I_DIRTY_TIME) == 0))
return 0;
return;
spin_lock(&inode->i_lock);
if (((inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW |
I_DIRTY_INODE)) == 0) &&
@ -4890,16 +4891,15 @@ static int other_inode_match(struct inode * inode, unsigned long ino,
spin_unlock(&inode->i_lock);
spin_lock(&ei->i_raw_lock);
EXT4_INODE_SET_XTIME(i_ctime, inode, oi->raw_inode);
EXT4_INODE_SET_XTIME(i_mtime, inode, oi->raw_inode);
EXT4_INODE_SET_XTIME(i_atime, inode, oi->raw_inode);
ext4_inode_csum_set(inode, oi->raw_inode, ei);
EXT4_INODE_SET_XTIME(i_ctime, inode, raw_inode);
EXT4_INODE_SET_XTIME(i_mtime, inode, raw_inode);
EXT4_INODE_SET_XTIME(i_atime, inode, raw_inode);
ext4_inode_csum_set(inode, raw_inode, ei);
spin_unlock(&ei->i_raw_lock);
trace_ext4_other_inode_update_time(inode, oi->orig_ino);
return -1;
trace_ext4_other_inode_update_time(inode, orig_ino);
return;
}
spin_unlock(&inode->i_lock);
return -1;
}
/*
@ -4909,24 +4909,24 @@ static int other_inode_match(struct inode * inode, unsigned long ino,
static void ext4_update_other_inodes_time(struct super_block *sb,
unsigned long orig_ino, char *buf)
{
struct other_inode oi;
unsigned long ino;
int i, inodes_per_block = EXT4_SB(sb)->s_inodes_per_block;
int inode_size = EXT4_INODE_SIZE(sb);
oi.orig_ino = orig_ino;
/*
* Calculate the first inode in the inode table block. Inode
* numbers are one-based. That is, the first inode in a block
* (assuming 4k blocks and 256 byte inodes) is (n*16 + 1).
*/
ino = ((orig_ino - 1) & ~(inodes_per_block - 1)) + 1;
rcu_read_lock();
for (i = 0; i < inodes_per_block; i++, ino++, buf += inode_size) {
if (ino == orig_ino)
continue;
oi.raw_inode = (struct ext4_inode *) buf;
(void) find_inode_nowait(sb, ino, other_inode_match, &oi);
__ext4_update_other_inode_time(sb, orig_ino, ino,
(struct ext4_inode *)buf);
}
rcu_read_unlock();
}
/*

Просмотреть файл

@ -497,7 +497,7 @@ void __insert_inode_hash(struct inode *inode, unsigned long hashval)
spin_lock(&inode_hash_lock);
spin_lock(&inode->i_lock);
hlist_add_head(&inode->i_hash, b);
hlist_add_head_rcu(&inode->i_hash, b);
spin_unlock(&inode->i_lock);
spin_unlock(&inode_hash_lock);
}
@ -513,7 +513,7 @@ void __remove_inode_hash(struct inode *inode)
{
spin_lock(&inode_hash_lock);
spin_lock(&inode->i_lock);
hlist_del_init(&inode->i_hash);
hlist_del_init_rcu(&inode->i_hash);
spin_unlock(&inode->i_lock);
spin_unlock(&inode_hash_lock);
}
@ -1107,7 +1107,7 @@ again:
*/
spin_lock(&inode->i_lock);
inode->i_state |= I_NEW;
hlist_add_head(&inode->i_hash, head);
hlist_add_head_rcu(&inode->i_hash, head);
spin_unlock(&inode->i_lock);
if (!creating)
inode_sb_list_add(inode);
@ -1201,7 +1201,7 @@ again:
inode->i_ino = ino;
spin_lock(&inode->i_lock);
inode->i_state = I_NEW;
hlist_add_head(&inode->i_hash, head);
hlist_add_head_rcu(&inode->i_hash, head);
spin_unlock(&inode->i_lock);
inode_sb_list_add(inode);
spin_unlock(&inode_hash_lock);
@ -1244,15 +1244,10 @@ static int test_inode_iunique(struct super_block *sb, unsigned long ino)
struct hlist_head *b = inode_hashtable + hash(sb, ino);
struct inode *inode;
spin_lock(&inode_hash_lock);
hlist_for_each_entry(inode, b, i_hash) {
if (inode->i_ino == ino && inode->i_sb == sb) {
spin_unlock(&inode_hash_lock);
hlist_for_each_entry_rcu(inode, b, i_hash) {
if (inode->i_ino == ino && inode->i_sb == sb)
return 0;
}
}
spin_unlock(&inode_hash_lock);
return 1;
}
@ -1281,6 +1276,7 @@ ino_t iunique(struct super_block *sb, ino_t max_reserved)
static unsigned int counter;
ino_t res;
rcu_read_lock();
spin_lock(&iunique_lock);
do {
if (counter <= max_reserved)
@ -1288,6 +1284,7 @@ ino_t iunique(struct super_block *sb, ino_t max_reserved)
res = counter++;
} while (!test_inode_iunique(sb, res));
spin_unlock(&iunique_lock);
rcu_read_unlock();
return res;
}
@ -1456,6 +1453,84 @@ out:
}
EXPORT_SYMBOL(find_inode_nowait);
/**
* find_inode_rcu - find an inode in the inode cache
* @sb: Super block of file system to search
* @hashval: Key to hash
* @test: Function to test match on an inode
* @data: Data for test function
*
* Search for the inode specified by @hashval and @data in the inode cache,
* where the helper function @test will return 0 if the inode does not match
* and 1 if it does. The @test function must be responsible for taking the
* i_lock spin_lock and checking i_state for an inode being freed or being
* initialized.
*
* If successful, this will return the inode for which the @test function
* returned 1 and NULL otherwise.
*
* The @test function is not permitted to take a ref on any inode presented.
* It is also not permitted to sleep.
*
* The caller must hold the RCU read lock.
*/
struct inode *find_inode_rcu(struct super_block *sb, unsigned long hashval,
int (*test)(struct inode *, void *), void *data)
{
struct hlist_head *head = inode_hashtable + hash(sb, hashval);
struct inode *inode;
RCU_LOCKDEP_WARN(!rcu_read_lock_held(),
"suspicious find_inode_rcu() usage");
hlist_for_each_entry_rcu(inode, head, i_hash) {
if (inode->i_sb == sb &&
!(READ_ONCE(inode->i_state) & (I_FREEING | I_WILL_FREE)) &&
test(inode, data))
return inode;
}
return NULL;
}
EXPORT_SYMBOL(find_inode_rcu);
/**
* find_inode_by_rcu - Find an inode in the inode cache
* @sb: Super block of file system to search
* @ino: The inode number to match
*
* Search for the inode specified by @hashval and @data in the inode cache,
* where the helper function @test will return 0 if the inode does not match
* and 1 if it does. The @test function must be responsible for taking the
* i_lock spin_lock and checking i_state for an inode being freed or being
* initialized.
*
* If successful, this will return the inode for which the @test function
* returned 1 and NULL otherwise.
*
* The @test function is not permitted to take a ref on any inode presented.
* It is also not permitted to sleep.
*
* The caller must hold the RCU read lock.
*/
struct inode *find_inode_by_ino_rcu(struct super_block *sb,
unsigned long ino)
{
struct hlist_head *head = inode_hashtable + hash(sb, ino);
struct inode *inode;
RCU_LOCKDEP_WARN(!rcu_read_lock_held(),
"suspicious find_inode_by_ino_rcu() usage");
hlist_for_each_entry_rcu(inode, head, i_hash) {
if (inode->i_ino == ino &&
inode->i_sb == sb &&
!(READ_ONCE(inode->i_state) & (I_FREEING | I_WILL_FREE)))
return inode;
}
return NULL;
}
EXPORT_SYMBOL(find_inode_by_ino_rcu);
int insert_inode_locked(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
@ -1480,7 +1555,7 @@ int insert_inode_locked(struct inode *inode)
if (likely(!old)) {
spin_lock(&inode->i_lock);
inode->i_state |= I_NEW | I_CREATING;
hlist_add_head(&inode->i_hash, head);
hlist_add_head_rcu(&inode->i_hash, head);
spin_unlock(&inode->i_lock);
spin_unlock(&inode_hash_lock);
return 0;
@ -1540,6 +1615,7 @@ static void iput_final(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
const struct super_operations *op = inode->i_sb->s_op;
unsigned long state;
int drop;
WARN_ON(inode->i_state & I_NEW);
@ -1555,16 +1631,20 @@ static void iput_final(struct inode *inode)
return;
}
state = inode->i_state;
if (!drop) {
inode->i_state |= I_WILL_FREE;
WRITE_ONCE(inode->i_state, state | I_WILL_FREE);
spin_unlock(&inode->i_lock);
write_inode_now(inode, 1);
spin_lock(&inode->i_lock);
WARN_ON(inode->i_state & I_NEW);
inode->i_state &= ~I_WILL_FREE;
state = inode->i_state;
WARN_ON(state & I_NEW);
state &= ~I_WILL_FREE;
}
inode->i_state |= I_FREEING;
WRITE_ONCE(inode->i_state, state | I_FREEING);
if (!list_empty(&inode->i_lru))
inode_lru_list_del(inode);
spin_unlock(&inode->i_lock);

Просмотреть файл

@ -3081,6 +3081,9 @@ extern struct inode *find_inode_nowait(struct super_block *,
int (*match)(struct inode *,
unsigned long, void *),
void *data);
extern struct inode *find_inode_rcu(struct super_block *, unsigned long,
int (*)(struct inode *, void *), void *);
extern struct inode *find_inode_by_ino_rcu(struct super_block *, unsigned long);
extern int insert_inode_locked4(struct inode *, unsigned long, int (*test)(struct inode *, void *), void *);
extern int insert_inode_locked(struct inode *);
#ifdef CONFIG_DEBUG_LOCK_ALLOC

Просмотреть файл

@ -33,20 +33,40 @@ enum afs_server_trace {
afs_server_trace_destroy,
afs_server_trace_free,
afs_server_trace_gc,
afs_server_trace_get_by_addr,
afs_server_trace_get_by_uuid,
afs_server_trace_get_caps,
afs_server_trace_get_install,
afs_server_trace_get_new_cbi,
afs_server_trace_get_probe,
afs_server_trace_give_up_cb,
afs_server_trace_put_call,
afs_server_trace_put_cbi,
afs_server_trace_put_find_rsq,
afs_server_trace_put_probe,
afs_server_trace_put_slist,
afs_server_trace_put_slist_isort,
afs_server_trace_put_uuid_rsq,
afs_server_trace_update,
};
enum afs_volume_trace {
afs_volume_trace_alloc,
afs_volume_trace_free,
afs_volume_trace_get_alloc_sbi,
afs_volume_trace_get_cell_insert,
afs_volume_trace_get_new_op,
afs_volume_trace_get_query_alias,
afs_volume_trace_put_cell_dup,
afs_volume_trace_put_cell_root,
afs_volume_trace_put_destroy_sbi,
afs_volume_trace_put_free_fc,
afs_volume_trace_put_put_op,
afs_volume_trace_put_query_alias,
afs_volume_trace_put_validate_fc,
afs_volume_trace_remove,
};
enum afs_fs_operation {
afs_FS_FetchData = 130, /* AFS Fetch file data */
afs_FS_FetchACL = 131, /* AFS Fetch file ACL */
@ -108,6 +128,7 @@ enum afs_vl_operation {
afs_VL_GetEntryByNameU = 527, /* AFS Get Vol Entry By Name operation ID */
afs_VL_GetAddrsU = 533, /* AFS Get FS server addresses */
afs_YFSVL_GetEndpoints = 64002, /* YFS Get FS & Vol server addresses */
afs_YFSVL_GetCellName = 64014, /* YFS Get actual cell name */
afs_VL_GetCapabilities = 65537, /* AFS Get VL server capabilities */
};
@ -140,6 +161,7 @@ enum afs_eproto_cause {
afs_eproto_bad_status,
afs_eproto_cb_count,
afs_eproto_cb_fid_count,
afs_eproto_cellname_len,
afs_eproto_file_type,
afs_eproto_ibulkst_cb_count,
afs_eproto_ibulkst_count,
@ -241,19 +263,38 @@ enum afs_cb_break_reason {
EM(afs_server_trace_destroy, "DESTROY ") \
EM(afs_server_trace_free, "FREE ") \
EM(afs_server_trace_gc, "GC ") \
EM(afs_server_trace_get_by_addr, "GET addr ") \
EM(afs_server_trace_get_by_uuid, "GET uuid ") \
EM(afs_server_trace_get_caps, "GET caps ") \
EM(afs_server_trace_get_install, "GET inst ") \
EM(afs_server_trace_get_new_cbi, "GET cbi ") \
EM(afs_server_trace_get_probe, "GET probe") \
EM(afs_server_trace_give_up_cb, "giveup-cb") \
EM(afs_server_trace_put_call, "PUT call ") \
EM(afs_server_trace_put_cbi, "PUT cbi ") \
EM(afs_server_trace_put_find_rsq, "PUT f-rsq") \
EM(afs_server_trace_put_probe, "PUT probe") \
EM(afs_server_trace_put_slist, "PUT slist") \
EM(afs_server_trace_put_slist_isort, "PUT isort") \
EM(afs_server_trace_put_uuid_rsq, "PUT u-req") \
E_(afs_server_trace_update, "UPDATE")
#define afs_volume_traces \
EM(afs_volume_trace_alloc, "ALLOC ") \
EM(afs_volume_trace_free, "FREE ") \
EM(afs_volume_trace_get_alloc_sbi, "GET sbi-alloc ") \
EM(afs_volume_trace_get_cell_insert, "GET cell-insrt") \
EM(afs_volume_trace_get_new_op, "GET op-new ") \
EM(afs_volume_trace_get_query_alias, "GET cell-alias") \
EM(afs_volume_trace_put_cell_dup, "PUT cell-dup ") \
EM(afs_volume_trace_put_cell_root, "PUT cell-root ") \
EM(afs_volume_trace_put_destroy_sbi, "PUT sbi-destry") \
EM(afs_volume_trace_put_free_fc, "PUT fc-free ") \
EM(afs_volume_trace_put_put_op, "PUT op-put ") \
EM(afs_volume_trace_put_query_alias, "PUT cell-alias") \
EM(afs_volume_trace_put_validate_fc, "PUT fc-validat") \
E_(afs_volume_trace_remove, "REMOVE ")
#define afs_fs_operations \
EM(afs_FS_FetchData, "FS.FetchData") \
EM(afs_FS_FetchStatus, "FS.FetchStatus") \
@ -310,6 +351,7 @@ enum afs_cb_break_reason {
EM(afs_VL_GetEntryByNameU, "VL.GetEntryByNameU") \
EM(afs_VL_GetAddrsU, "VL.GetAddrsU") \
EM(afs_YFSVL_GetEndpoints, "YFSVL.GetEndpoints") \
EM(afs_YFSVL_GetCellName, "YFSVL.GetCellName") \
E_(afs_VL_GetCapabilities, "VL.GetCapabilities")
#define afs_edit_dir_ops \
@ -339,6 +381,7 @@ enum afs_cb_break_reason {
EM(afs_eproto_bad_status, "BadStatus") \
EM(afs_eproto_cb_count, "CbCount") \
EM(afs_eproto_cb_fid_count, "CbFidCount") \
EM(afs_eproto_cellname_len, "CellNameLen") \
EM(afs_eproto_file_type, "FileTYpe") \
EM(afs_eproto_ibulkst_cb_count, "IBS.CbCount") \
EM(afs_eproto_ibulkst_count, "IBS.FidCount") \
@ -636,7 +679,7 @@ TRACE_EVENT(afs_make_fs_calli,
TRACE_EVENT(afs_make_fs_call1,
TP_PROTO(struct afs_call *call, const struct afs_fid *fid,
const char *name),
const struct qstr *name),
TP_ARGS(call, fid, name),
@ -648,8 +691,7 @@ TRACE_EVENT(afs_make_fs_call1,
),
TP_fast_assign(
int __len = strlen(name);
__len = min(__len, 23);
unsigned int __len = min_t(unsigned int, name->len, 23);
__entry->call = call->debug_id;
__entry->op = call->operation_ID;
if (fid) {
@ -659,7 +701,7 @@ TRACE_EVENT(afs_make_fs_call1,
__entry->fid.vnode = 0;
__entry->fid.unique = 0;
}
memcpy(__entry->name, name, __len);
memcpy(__entry->name, name->name, __len);
__entry->name[__len] = 0;
),
@ -674,7 +716,7 @@ TRACE_EVENT(afs_make_fs_call1,
TRACE_EVENT(afs_make_fs_call2,
TP_PROTO(struct afs_call *call, const struct afs_fid *fid,
const char *name, const char *name2),
const struct qstr *name, const struct qstr *name2),
TP_ARGS(call, fid, name, name2),
@ -687,10 +729,8 @@ TRACE_EVENT(afs_make_fs_call2,
),
TP_fast_assign(
int __len = strlen(name);
int __len2 = strlen(name2);
__len = min(__len, 23);
__len2 = min(__len2, 23);
unsigned int __len = min_t(unsigned int, name->len, 23);
unsigned int __len2 = min_t(unsigned int, name2->len, 23);
__entry->call = call->debug_id;
__entry->op = call->operation_ID;
if (fid) {
@ -700,9 +740,9 @@ TRACE_EVENT(afs_make_fs_call2,
__entry->fid.vnode = 0;
__entry->fid.unique = 0;
}
memcpy(__entry->name, name, __len);
memcpy(__entry->name, name->name, __len);
__entry->name[__len] = 0;
memcpy(__entry->name2, name2, __len2);
memcpy(__entry->name2, name2->name, __len2);
__entry->name2[__len2] = 0;
),
@ -988,24 +1028,22 @@ TRACE_EVENT(afs_edit_dir,
);
TRACE_EVENT(afs_protocol_error,
TP_PROTO(struct afs_call *call, int error, enum afs_eproto_cause cause),
TP_PROTO(struct afs_call *call, enum afs_eproto_cause cause),
TP_ARGS(call, error, cause),
TP_ARGS(call, cause),
TP_STRUCT__entry(
__field(unsigned int, call )
__field(int, error )
__field(enum afs_eproto_cause, cause )
),
TP_fast_assign(
__entry->call = call ? call->debug_id : 0;
__entry->error = error;
__entry->cause = cause;
),
TP_printk("c=%08x r=%d %s",
__entry->call, __entry->error,
TP_printk("c=%08x %s",
__entry->call,
__print_symbolic(__entry->cause, afs_eproto_causes))
);
@ -1271,26 +1309,53 @@ TRACE_EVENT(afs_cb_miss,
);
TRACE_EVENT(afs_server,
TP_PROTO(struct afs_server *server, int usage, enum afs_server_trace reason),
TP_PROTO(struct afs_server *server, int ref, int active,
enum afs_server_trace reason),
TP_ARGS(server, usage, reason),
TP_ARGS(server, ref, active, reason),
TP_STRUCT__entry(
__field(unsigned int, server )
__field(int, usage )
__field(int, ref )
__field(int, active )
__field(int, reason )
),
TP_fast_assign(
__entry->server = server->debug_id;
__entry->usage = usage;
__entry->ref = ref;
__entry->active = active;
__entry->reason = reason;
),
TP_printk("s=%08x %s u=%d",
TP_printk("s=%08x %s u=%d a=%d",
__entry->server,
__print_symbolic(__entry->reason, afs_server_traces),
__entry->usage)
__entry->ref,
__entry->active)
);
TRACE_EVENT(afs_volume,
TP_PROTO(afs_volid_t vid, int ref, enum afs_volume_trace reason),
TP_ARGS(vid, ref, reason),
TP_STRUCT__entry(
__field(afs_volid_t, vid )
__field(int, ref )
__field(enum afs_volume_trace, reason )
),
TP_fast_assign(
__entry->vid = vid;
__entry->ref = ref;
__entry->reason = reason;
),
TP_printk("V=%llx %s u=%d",
__entry->vid,
__print_symbolic(__entry->reason, afs_volume_traces),
__entry->ref)
);
#endif /* _TRACE_AFS_H */

Просмотреть файл

@ -271,6 +271,9 @@ static void rxrpc_store_error(struct rxrpc_peer *peer,
break;
case SO_EE_ORIGIN_ICMP6:
if (err == EACCES)
err = EHOSTUNREACH;
/* Fall through */
default:
_proto("Rx Received error report { orig=%u }", ee->ee_origin);
break;

Просмотреть файл

@ -68,7 +68,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
"Proto Local "
" Remote "
" SvID ConnID CallID End Use State Abort "
" UserID TxSeq TW RxSeq RW RxSerial RxTimo\n");
" DebugId TxSeq TW RxSeq RW RxSerial RxTimo\n");
return 0;
}
@ -100,7 +100,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
rx_hard_ack = READ_ONCE(call->rx_hard_ack);
seq_printf(seq,
"UDP %-47.47s %-47.47s %4x %08x %08x %s %3u"
" %-8.8s %08x %lx %08x %02x %08x %02x %08x %06lx\n",
" %-8.8s %08x %08x %08x %02x %08x %02x %08x %06lx\n",
lbuff,
rbuff,
call->service_id,
@ -110,7 +110,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
atomic_read(&call->usage),
rxrpc_call_states[call->state],
call->abort_code,
call->user_call_ID,
call->debug_id,
tx_hard_ack, READ_ONCE(call->tx_top) - tx_hard_ack,
rx_hard_ack, READ_ONCE(call->rx_top) - rx_hard_ack,
call->rx_serial,