Skip to content
19 changes: 17 additions & 2 deletions kmod/src/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "trans.h"
#include "alloc.h"
#include "counters.h"
#include "msg.h"
#include "scoutfs_trace.h"

/*
Expand Down Expand Up @@ -496,10 +497,11 @@ static int dirty_alloc_blocks(struct super_block *sb,
struct scoutfs_block *fr_bl = NULL;
struct scoutfs_block *bl;
bool link_orig = false;
__le32 orig_first_nr;
u64 av_peek;
u64 av_old;
u64 av_old = 0;
u64 fr_peek;
u64 fr_old;
u64 fr_old = 0;
int ret;

if (alloc->dirty_avail_bl != NULL)
Expand All @@ -509,6 +511,7 @@ static int dirty_alloc_blocks(struct super_block *sb,

/* undo dirty freed if we get an error after */
orig_freed = alloc->freed.ref;
orig_first_nr = alloc->freed.first_nr;

if (alloc->dirty_avail_bl != NULL) {
ret = 0;
Expand Down Expand Up @@ -562,6 +565,17 @@ static int dirty_alloc_blocks(struct super_block *sb,
/* sort dirty avail to encourage contiguous sorted meta blocks */
list_block_sort(av_bl->data);

lblk = fr_bl->data;
if (WARN_ON_ONCE(alloc->freed.ref.blkno != lblk->hdr.blkno)) {
scoutfs_err(sb, "dirty_alloc freed ref %llu hdr %llu av_old %llu fr_old %llu av_peek %llu fr_peek %llu link_orig %d",
le64_to_cpu(alloc->freed.ref.blkno),
le64_to_cpu(lblk->hdr.blkno),
av_old, fr_old, av_peek, fr_peek, link_orig);
ret = -EIO;
goto out;
}
lblk = NULL;

if (av_old)
list_block_add(&alloc->freed, fr_bl->data, av_old);
if (fr_old)
Expand All @@ -578,6 +592,7 @@ static int dirty_alloc_blocks(struct super_block *sb,
if (fr_bl)
scoutfs_block_writer_forget(sb, wri, fr_bl);
alloc->freed.ref = orig_freed;
alloc->freed.first_nr = orig_first_nr;
}

mutex_unlock(&alloc->mutex);
Expand Down
10 changes: 8 additions & 2 deletions kmod/src/block.c
Original file line number Diff line number Diff line change
Expand Up @@ -624,11 +624,15 @@ static struct block_private *block_read(struct super_block *sb, u64 blkno)
if (!test_bit(BLOCK_BIT_UPTODATE, &bp->bits) &&
test_and_clear_bit(BLOCK_BIT_NEW, &bp->bits)) {
ret = block_submit_bio(sb, bp, REQ_OP_READ);
if (ret < 0)
if (ret < 0) {
set_bit(BLOCK_BIT_ERROR, &bp->bits);
goto out;
}
}

wait_event(binf->waitq, uptodate_or_error(bp));
while (!wait_event_timeout(binf->waitq, uptodate_or_error(bp), 120 * HZ))
WARN(1, "block read blkno %llu waiting for bio completion\n",
bp->bl.blkno);
if (test_bit(BLOCK_BIT_ERROR, &bp->bits))
ret = -EIO;
else
Expand Down Expand Up @@ -836,6 +840,8 @@ int scoutfs_block_dirty_ref(struct super_block *sb, struct scoutfs_alloc *alloc,
bp = BLOCK_PRIVATE(bl);

if (block_is_dirty(bp)) {
if (ref_blkno)
*ref_blkno = 0;
ret = 0;
goto out;
}
Expand Down
8 changes: 6 additions & 2 deletions kmod/src/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -646,8 +646,12 @@ void scoutfs_client_destroy(struct super_block *sb)
client_farewell_response,
NULL, NULL);
if (ret == 0) {
wait_for_completion(&client->farewell_comp);
ret = client->farewell_error;
if (!wait_for_completion_timeout(&client->farewell_comp,
120 * HZ)) {
ret = -ETIMEDOUT;
} else {
ret = client->farewell_error;
}
}
if (ret) {
scoutfs_inc_counter(sb, client_farewell_error);
Expand Down
73 changes: 49 additions & 24 deletions kmod/src/lock.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
* relative to that lock state we resend.
*/

#define CLIENT_LOCK_WAIT_TIMEOUT (60 * HZ)

/*
* allocated per-super, freed on unmount.
*/
Expand Down Expand Up @@ -157,6 +159,33 @@ static void invalidate_inode(struct super_block *sb, u64 ino)
}
}

/*
* Remove all coverage items from the lock to tell users that their
* cache is stale. This is lock-internal bookkeeping that is safe to
* call during shutdown and unmount. The unconditional unlock/relock
* of cov_list_lock avoids sparse warnings from unbalanced locking in
* the trylock failure path.
*/
static void lock_clear_coverage(struct super_block *sb,
struct scoutfs_lock *lock)
{
struct scoutfs_lock_coverage *cov;

spin_lock(&lock->cov_list_lock);
while ((cov = list_first_entry_or_null(&lock->cov_list,
struct scoutfs_lock_coverage, head))) {
if (spin_trylock(&cov->cov_lock)) {
list_del_init(&cov->head);
cov->lock = NULL;
spin_unlock(&cov->cov_lock);
scoutfs_inc_counter(sb, lock_invalidate_coverage);
}
spin_unlock(&lock->cov_list_lock);
spin_lock(&lock->cov_list_lock);
}
spin_unlock(&lock->cov_list_lock);
}

/*
* Invalidate caches associated with this lock. Either we're
* invalidating a write to a read or we're invalidating to null. We
Expand All @@ -166,7 +195,6 @@ static void invalidate_inode(struct super_block *sb, u64 ino)
static int lock_invalidate(struct super_block *sb, struct scoutfs_lock *lock,
enum scoutfs_lock_mode prev, enum scoutfs_lock_mode mode)
{
struct scoutfs_lock_coverage *cov;
u64 ino, last;
int ret = 0;

Expand All @@ -190,24 +218,7 @@ static int lock_invalidate(struct super_block *sb, struct scoutfs_lock *lock,

/* have to invalidate if we're not in the only usable case */
if (!(prev == SCOUTFS_LOCK_WRITE && mode == SCOUTFS_LOCK_READ)) {
/*
* Remove cov items to tell users that their cache is
* stale. The unlock pattern comes from avoiding bad
* sparse warnings when taking else in a failed trylock.
*/
spin_lock(&lock->cov_list_lock);
while ((cov = list_first_entry_or_null(&lock->cov_list,
struct scoutfs_lock_coverage, head))) {
if (spin_trylock(&cov->cov_lock)) {
list_del_init(&cov->head);
cov->lock = NULL;
spin_unlock(&cov->cov_lock);
scoutfs_inc_counter(sb, lock_invalidate_coverage);
}
spin_unlock(&lock->cov_list_lock);
spin_lock(&lock->cov_list_lock);
}
spin_unlock(&lock->cov_list_lock);
lock_clear_coverage(sb, lock);

/* invalidate inodes after removing coverage so drop/evict aren't covered */
if (lock->start.sk_zone == SCOUTFS_FS_ZONE) {
Expand Down Expand Up @@ -714,10 +725,12 @@ static void lock_invalidate_worker(struct work_struct *work)
ireq = list_first_entry(&lock->inv_list, struct inv_req, head);
nl = &ireq->nl;

/* only lock protocol, inv can't call subsystems after shutdown */
if (!linfo->shutdown) {
/* only lock protocol, inv can't call subsystems after shutdown or unmount */
if (!linfo->shutdown && !scoutfs_unmounting(sb)) {
ret = lock_invalidate(sb, lock, nl->old_mode, nl->new_mode);
BUG_ON(ret < 0 && ret != -ENOLINK);
} else {
lock_clear_coverage(sb, lock);
}

/* respond with the key and modes from the request, server might have died */
Expand Down Expand Up @@ -953,6 +966,9 @@ static bool lock_wait_cond(struct super_block *sb, struct scoutfs_lock *lock,
!lock->request_pending;
spin_unlock(&linfo->lock);

if (!wake)
wake = scoutfs_unmounting(sb);

if (!wake)
scoutfs_inc_counter(sb, lock_wait);

Expand Down Expand Up @@ -997,8 +1013,10 @@ static int lock_key_range(struct super_block *sb, enum scoutfs_lock_mode mode, i
return -EINVAL;

/* maybe catch _setup() and _shutdown order mistakes */
if (WARN_ON_ONCE(!linfo || linfo->shutdown))
if (!linfo || linfo->shutdown) {
WARN_ON_ONCE(!scoutfs_unmounting(sb));
return -ENOLCK;
}

/* have to lock before entering transactions */
if (WARN_ON_ONCE(scoutfs_trans_held()))
Expand All @@ -1024,6 +1042,11 @@ static int lock_key_range(struct super_block *sb, enum scoutfs_lock_mode mode, i
break;
}

if (scoutfs_unmounting(sb)) {
ret = -ESHUTDOWN;
break;
}

/* the fast path where we can use the granted mode */
if (lock_modes_match(lock->mode, mode)) {
lock_inc_count(lock->users, mode);
Expand Down Expand Up @@ -1067,8 +1090,9 @@ static int lock_key_range(struct super_block *sb, enum scoutfs_lock_mode mode, i
if (flags & SCOUTFS_LKF_INTERRUPTIBLE) {
ret = wait_event_interruptible(lock->waitq,
lock_wait_cond(sb, lock, mode));
} else {
wait_event(lock->waitq, lock_wait_cond(sb, lock, mode));
} else if (!wait_event_timeout(lock->waitq,
lock_wait_cond(sb, lock, mode),
CLIENT_LOCK_WAIT_TIMEOUT)) {
ret = 0;
}

Expand Down Expand Up @@ -1650,6 +1674,7 @@ void scoutfs_lock_destroy(struct super_block *sb)
list_del_init(&lock->inv_head);
lock->invalidate_pending = 0;
}
lock_clear_coverage(sb, lock);
lock_remove(linfo, lock);
lock_free(linfo, lock);
}
Expand Down
23 changes: 19 additions & 4 deletions kmod/src/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -1990,8 +1990,9 @@ static int sync_response(struct super_block *sb,
* buffer. Errors returned can come from the remote request processing
* or local failure to send.
*
* The wait for the response is interruptible and can return
* -ERESTARTSYS if it is interrupted.
* The wait for the response uses a 60 second timeout loop that
* checks for unmount, returning -ESHUTDOWN if the mount is
* being torn down.
*
* -EOVERFLOW is returned if the response message's data_length doesn't
* match the caller's resp_len buffer.
Expand All @@ -2002,6 +2003,7 @@ int scoutfs_net_sync_request(struct super_block *sb,
void *resp, size_t resp_len)
{
struct sync_request_completion sreq;
struct message_send *msend;
int ret;
u64 id;

Expand All @@ -2014,8 +2016,21 @@ int scoutfs_net_sync_request(struct super_block *sb,
sync_response, &sreq, &id);

if (ret == 0) {
wait_for_completion(&sreq.comp);
ret = sreq.error;
while (!wait_for_completion_timeout(&sreq.comp, 60 * HZ)) {
if (scoutfs_unmounting(sb)) {
ret = -ESHUTDOWN;
break;
}
}
if (ret == -ESHUTDOWN) {
spin_lock(&conn->lock);
msend = find_request(conn, cmd, id);
if (msend)
queue_dead_free(conn, msend);
spin_unlock(&conn->lock);
} else {
ret = sreq.error;
}
}

return ret;
Expand Down
2 changes: 1 addition & 1 deletion kmod/src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ static void scoutfs_server_commit_func(struct work_struct *work)
ret = scoutfs_alloc_empty_list(sb, &server->alloc, &server->wri,
server->meta_freed,
server->other_freed);
if (ret) {
if (ret && ret != -ENOLINK) {
scoutfs_err(sb, "server error emptying freed: %d", ret);
goto out;
}
Expand Down
3 changes: 2 additions & 1 deletion kmod/src/trans.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ static int retry_forever(struct super_block *sb, int (*func)(struct super_block
retrying = true;
}

if (scoutfs_forcing_unmount(sb)) {
if (scoutfs_forcing_unmount(sb) ||
scoutfs_unmounting(sb)) {
ret = -ENOLINK;
break;
}
Expand Down