diff --git a/kmod/src/client.c b/kmod/src/client.c index 38cf3ec7..4799fcc5 100644 --- a/kmod/src/client.c +++ b/kmod/src/client.c @@ -668,3 +668,11 @@ void scoutfs_client_destroy(struct super_block *sb) kfree(client); sbi->client_info = NULL; } + +void scoutfs_client_net_shutdown(struct super_block *sb) +{ + struct client_info *client = SCOUTFS_SB(sb)->client_info; + + if (client && client->conn) + scoutfs_net_shutdown(sb, client->conn); +} diff --git a/kmod/src/client.h b/kmod/src/client.h index a360da1c..bb155fe0 100644 --- a/kmod/src/client.h +++ b/kmod/src/client.h @@ -35,6 +35,7 @@ int scoutfs_client_clear_volopt(struct super_block *sb, struct scoutfs_volume_op int scoutfs_client_resize_devices(struct super_block *sb, struct scoutfs_net_resize_devices *nrd); int scoutfs_client_statfs(struct super_block *sb, struct scoutfs_net_statfs *nst); +void scoutfs_client_net_shutdown(struct super_block *sb); int scoutfs_client_setup(struct super_block *sb); void scoutfs_client_destroy(struct super_block *sb); diff --git a/kmod/src/net.c b/kmod/src/net.c index 45e907ac..e028ce22 100644 --- a/kmod/src/net.c +++ b/kmod/src/net.c @@ -835,17 +835,9 @@ static void scoutfs_net_destroy_worker(struct work_struct *work) if (conn->listening_conn && conn->notify_down) conn->notify_down(sb, conn, conn->info, conn->rid); - /* - * Usually networking is idle and we destroy pending sends, but when forcing unmount - * we can have to wake up waiters by failing pending sends. - */ list_splice_init(&conn->resend_queue, &conn->send_queue); - list_for_each_entry_safe(msend, tmp, &conn->send_queue, head) { - if (scoutfs_forcing_unmount(sb)) - call_resp_func(sb, conn, msend->resp_func, msend->resp_data, - NULL, 0, -ECONNABORTED); + list_for_each_entry_safe(msend, tmp, &conn->send_queue, head) free_msend(ninf, msend); - } /* accepted sockets are removed from their listener's list */ if (conn->listening_conn) { @@ -1134,9 +1126,11 @@ static void scoutfs_net_shutdown_worker(struct work_struct *work) struct net_info *ninf = SCOUTFS_SB(sb)->net_info; struct scoutfs_net_connection *listener; struct scoutfs_net_connection *acc_conn; + scoutfs_net_response_t resp_func; struct message_send *msend; struct message_send *tmp; unsigned long delay; + void *resp_data; trace_scoutfs_net_shutdown_work_enter(sb, 0, 0); trace_scoutfs_conn_shutdown_start(conn); @@ -1182,6 +1176,30 @@ static void scoutfs_net_shutdown_worker(struct work_struct *work) /* and wait for accepted conn shutdown work to finish */ wait_event(conn->waitq, empty_accepted_list(conn)); + /* + * Forced unmount will cause net submit to fail once it's + * started and it calls shutdown to interrupt any previous + * senders waiting for a response. The response callbacks can + * do quite a lot of work so we're careful to call them outside + * the lock. + */ + if (scoutfs_forcing_unmount(sb)) { + spin_lock(&conn->lock); + list_splice_tail_init(&conn->send_queue, &conn->resend_queue); + while ((msend = list_first_entry_or_null(&conn->resend_queue, + struct message_send, head))) { + resp_func = msend->resp_func; + resp_data = msend->resp_data; + free_msend(ninf, msend); + spin_unlock(&conn->lock); + + call_resp_func(sb, conn, resp_func, resp_data, NULL, 0, -ECONNABORTED); + + spin_lock(&conn->lock); + } + spin_unlock(&conn->lock); + } + spin_lock(&conn->lock); /* greetings aren't resent across sockets */ diff --git a/kmod/src/super.c b/kmod/src/super.c index 6dcb244f..792b8f1a 100644 --- a/kmod/src/super.c +++ b/kmod/src/super.c @@ -271,6 +271,8 @@ static void scoutfs_umount_begin(struct super_block *sb) scoutfs_warn(sb, "forcing unmount, can return errors and lose unsynced data"); sbi->forced_unmount = true; + + scoutfs_client_net_shutdown(sb); } static const struct super_operations scoutfs_super_ops = {