Skip to content

Commit

Permalink
Merge tag 'afs-fixes-20180514' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…kernel/git/dhowells/linux-fs

Pull AFS fixes from David Howells:
 "Here's a set of patches that fix a number of bugs in the in-kernel AFS
  client, including:

   - Fix directory locking to not use individual page locks for
     directory reading/scanning but rather to use a semaphore on the
     afs_vnode struct as the directory contents must be read in a single
     blob and data from different reads must not be mixed as the entire
     contents may be shuffled about between reads.

   - Fix address list parsing to handle port specifiers correctly.

   - Only give up callback records on a server if we actually talked to
     that server (we might not be able to access a server).

   - Fix some callback handling bugs, including refcounting,
     whole-volume callbacks and when callbacks actually get broken in
     response to a CB.CallBack op.

   - Fix some server/address rotation bugs, including giving up if we
     can't probe a server; giving up if a server says it doesn't have a
     volume, but there are more servers to try.

   - Fix the decoding of fetched statuses to be OpenAFS compatible.

   - Fix the handling of server lookups in Cache Manager ops (such as
     CB.InitCallBackState3) to use a UUID if possible and to handle no
     server being found.

   - Fix a bug in server lookup where not all addresses are compared.

   - Fix the non-encryption of calls that prevents some servers from
     being accessed (this also requires an AF_RXRPC patch that has
     already gone in through the net tree).

  There's also a patch that adds tracepoints to log Cache Manager ops
  that don't find a matching server, either by UUID or by address"

* tag 'afs-fixes-20180514' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  afs: Fix the non-encryption of calls
  afs: Fix CB.CallBack handling
  afs: Fix whole-volume callback handling
  afs: Fix afs_find_server search loop
  afs: Fix the handling of an unfound server in CM operations
  afs: Add a tracepoint to record callbacks from unlisted servers
  afs: Fix the handling of CB.InitCallBackState3 to find the server by UUID
  afs: Fix VNOVOL handling in address rotation
  afs: Fix AFSFetchStatus decoder to provide OpenAFS compatibility
  afs: Fix server rotation's handling of fileserver probe failure
  afs: Fix refcounting in callback registration
  afs: Fix giving up callbacks on server destruction
  afs: Fix address list parsing
  afs: Fix directory page locking
  • Loading branch information
torvalds committed May 15, 2018
2 parents eeba2df + 4776cab commit 21b9f1c
Show file tree
Hide file tree
Showing 17 changed files with 266 additions and 168 deletions.
25 changes: 15 additions & 10 deletions fs/afs/addr_list.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len,
p = text;
do {
struct sockaddr_rxrpc *srx = &alist->addrs[alist->nr_addrs];
char tdelim = delim;
const char *q, *stop;

if (*p == delim) {
p++;
Expand All @@ -130,28 +130,33 @@ struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len,

if (*p == '[') {
p++;
tdelim = ']';
q = memchr(p, ']', end - p);
} else {
for (q = p; q < end; q++)
if (*q == '+' || *q == delim)
break;
}

if (in4_pton(p, end - p,
if (in4_pton(p, q - p,
(u8 *)&srx->transport.sin6.sin6_addr.s6_addr32[3],
tdelim, &p)) {
-1, &stop)) {
srx->transport.sin6.sin6_addr.s6_addr32[0] = 0;
srx->transport.sin6.sin6_addr.s6_addr32[1] = 0;
srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
} else if (in6_pton(p, end - p,
} else if (in6_pton(p, q - p,
srx->transport.sin6.sin6_addr.s6_addr,
tdelim, &p)) {
-1, &stop)) {
/* Nothing to do */
} else {
goto bad_address;
}

if (tdelim == ']') {
if (p == end || *p != ']')
goto bad_address;
if (stop != q)
goto bad_address;

p = q;
if (q < end && *q == ']')
p++;
}

if (p < end) {
if (*p == '+') {
Expand Down
84 changes: 61 additions & 23 deletions fs/afs/callback.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,36 +23,55 @@
/*
* Set up an interest-in-callbacks record for a volume on a server and
* register it with the server.
* - Called with volume->server_sem held.
* - Called with vnode->io_lock held.
*/
int afs_register_server_cb_interest(struct afs_vnode *vnode,
struct afs_server_entry *entry)
struct afs_server_list *slist,
unsigned int index)
{
struct afs_cb_interest *cbi = entry->cb_interest, *vcbi, *new, *x;
struct afs_server_entry *entry = &slist->servers[index];
struct afs_cb_interest *cbi, *vcbi, *new, *old;
struct afs_server *server = entry->server;

again:
if (vnode->cb_interest &&
likely(vnode->cb_interest == entry->cb_interest))
return 0;

read_lock(&slist->lock);
cbi = afs_get_cb_interest(entry->cb_interest);
read_unlock(&slist->lock);

vcbi = vnode->cb_interest;
if (vcbi) {
if (vcbi == cbi)
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);
vnode->cb_interest = afs_get_cb_interest(cbi);
old = vnode->cb_interest;
vnode->cb_interest = cbi;
write_sequnlock(&vnode->cb_lock);
afs_put_cb_interest(afs_v2net(vnode), cbi);
afs_put_cb_interest(afs_v2net(vnode), old);
return 0;
}

/* Re-use the one attached to the vnode. */
if (!cbi && vcbi->server == server) {
afs_get_cb_interest(vcbi);
x = cmpxchg(&entry->cb_interest, cbi, vcbi);
if (x != cbi) {
cbi = x;
afs_put_cb_interest(afs_v2net(vnode), vcbi);
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;
}
}
Expand All @@ -72,13 +91,16 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
list_add_tail(&new->cb_link, &server->cb_interests);
write_unlock(&server->cb_break_lock);

x = cmpxchg(&entry->cb_interest, cbi, new);
if (x == cbi) {
write_lock(&slist->lock);
if (!entry->cb_interest) {
entry->cb_interest = afs_get_cb_interest(new);
cbi = new;
new = NULL;
} else {
cbi = x;
afs_put_cb_interest(afs_v2net(vnode), new);
cbi = afs_get_cb_interest(entry->cb_interest);
}
write_unlock(&slist->lock);
afs_put_cb_interest(afs_v2net(vnode), new);
}

ASSERT(cbi);
Expand All @@ -88,11 +110,14 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
*/
write_seqlock(&vnode->cb_lock);

vnode->cb_interest = afs_get_cb_interest(cbi);
old = vnode->cb_interest;
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;
}

Expand Down Expand Up @@ -171,13 +196,24 @@ static void afs_break_one_callback(struct afs_server *server,
if (cbi->vid != fid->vid)
continue;

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);
iput(inode);
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_break_lock);
volume->cb_v_break++;
write_unlock(&volume->cb_break_lock);
} 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);
iput(inode);
}
}
}

Expand All @@ -195,6 +231,8 @@ void afs_break_callbacks(struct afs_server *server, size_t count,
ASSERT(server != NULL);
ASSERTCMP(count, <=, AFSCBMAX);

/* TODO: Sort the callback break list by volume ID */

for (; count > 0; callbacks++, count--) {
_debug("- Fid { vl=%08x n=%u u=%u } CB { v=%u x=%u t=%u }",
callbacks->fid.vid,
Expand Down
67 changes: 21 additions & 46 deletions fs/afs/cmservice.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,21 +133,10 @@ bool afs_cm_incoming_call(struct afs_call *call)
}

/*
* clean up a cache manager call
* Clean up a cache manager call.
*/
static void afs_cm_destructor(struct afs_call *call)
{
_enter("");

/* Break the callbacks here so that we do it after the final ACK is
* received. The step number here must match the final number in
* afs_deliver_cb_callback().
*/
if (call->unmarshall == 5) {
ASSERT(call->cm_server && call->count && call->request);
afs_break_callbacks(call->cm_server, call->count, call->request);
}

kfree(call->buffer);
call->buffer = NULL;
}
Expand All @@ -161,14 +150,14 @@ static void SRXAFSCB_CallBack(struct work_struct *work)

_enter("");

/* be sure to send the reply *before* attempting to spam the AFS server
* with FSFetchStatus requests on the vnodes with broken callbacks lest
* the AFS server get into a vicious cycle of trying to break further
* callbacks because it hadn't received completion of the CBCallBack op
* yet */
afs_send_empty_reply(call);
/* We need to break the callbacks before sending the reply as the
* server holds up change visibility till it receives our reply so as
* to maintain cache coherency.
*/
if (call->cm_server)
afs_break_callbacks(call->cm_server, call->count, call->request);

afs_break_callbacks(call->cm_server, call->count, call->request);
afs_send_empty_reply(call);
afs_put_call(call);
_leave("");
}
Expand All @@ -180,7 +169,6 @@ static int afs_deliver_cb_callback(struct afs_call *call)
{
struct afs_callback_break *cb;
struct sockaddr_rxrpc srx;
struct afs_server *server;
__be32 *bp;
int ret, loop;

Expand Down Expand Up @@ -267,15 +255,6 @@ static int afs_deliver_cb_callback(struct afs_call *call)

call->offset = 0;
call->unmarshall++;

/* Record that the message was unmarshalled successfully so
* that the call destructor can know do the callback breaking
* work, even if the final ACK isn't received.
*
* If the step number changes, then afs_cm_destructor() must be
* updated also.
*/
call->unmarshall++;
case 5:
break;
}
Expand All @@ -286,10 +265,9 @@ static int afs_deliver_cb_callback(struct afs_call *call)
/* we'll need the file server record as that tells us which set of
* vnodes to operate upon */
rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
server = afs_find_server(call->net, &srx);
if (!server)
return -ENOTCONN;
call->cm_server = server;
call->cm_server = afs_find_server(call->net, &srx);
if (!call->cm_server)
trace_afs_cm_no_server(call, &srx);

return afs_queue_call_work(call);
}
Expand All @@ -303,7 +281,8 @@ static void SRXAFSCB_InitCallBackState(struct work_struct *work)

_enter("{%p}", call->cm_server);

afs_init_callback_state(call->cm_server);
if (call->cm_server)
afs_init_callback_state(call->cm_server);
afs_send_empty_reply(call);
afs_put_call(call);
_leave("");
Expand All @@ -315,7 +294,6 @@ static void SRXAFSCB_InitCallBackState(struct work_struct *work)
static int afs_deliver_cb_init_call_back_state(struct afs_call *call)
{
struct sockaddr_rxrpc srx;
struct afs_server *server;
int ret;

_enter("");
Expand All @@ -328,10 +306,9 @@ static int afs_deliver_cb_init_call_back_state(struct afs_call *call)

/* we'll need the file server record as that tells us which set of
* vnodes to operate upon */
server = afs_find_server(call->net, &srx);
if (!server)
return -ENOTCONN;
call->cm_server = server;
call->cm_server = afs_find_server(call->net, &srx);
if (!call->cm_server)
trace_afs_cm_no_server(call, &srx);

return afs_queue_call_work(call);
}
Expand All @@ -341,8 +318,6 @@ static int afs_deliver_cb_init_call_back_state(struct afs_call *call)
*/
static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)
{
struct sockaddr_rxrpc srx;
struct afs_server *server;
struct afs_uuid *r;
unsigned loop;
__be32 *b;
Expand Down Expand Up @@ -398,11 +373,11 @@ static int afs_deliver_cb_init_call_back_state3(struct afs_call *call)

/* we'll need the file server record as that tells us which set of
* vnodes to operate upon */
rxrpc_kernel_get_peer(call->net->socket, call->rxcall, &srx);
server = afs_find_server(call->net, &srx);
if (!server)
return -ENOTCONN;
call->cm_server = server;
rcu_read_lock();
call->cm_server = afs_find_server_by_uuid(call->net, call->request);
rcu_read_unlock();
if (!call->cm_server)
trace_afs_cm_no_server_u(call, call->request);

return afs_queue_call_work(call);
}
Expand Down
Loading

0 comments on commit 21b9f1c

Please sign in to comment.