diff --git a/kmod/src/client.c b/kmod/src/client.c index 81fd7fb7..abb3844f 100644 --- a/kmod/src/client.c +++ b/kmod/src/client.c @@ -361,7 +361,8 @@ out: * out and fails. */ static void client_notify_down(struct super_block *sb, - struct scoutfs_net_connection *conn) + struct scoutfs_net_connection *conn, void *info, + u64 node_id) { struct client_info *client = SCOUTFS_SB(sb)->client_info; @@ -404,7 +405,7 @@ int scoutfs_client_setup(struct super_block *sb) scoutfs_client_connect_worker); /* client doesn't process any incoming requests yet */ - client->conn = scoutfs_net_alloc_conn(sb, NULL, client_notify_down, + client->conn = scoutfs_net_alloc_conn(sb, NULL, client_notify_down, 0, NULL, "client"); if (!client->conn) { ret = -ENOMEM; diff --git a/kmod/src/net.c b/kmod/src/net.c index 42efe341..c26c428a 100644 --- a/kmod/src/net.c +++ b/kmod/src/net.c @@ -73,6 +73,7 @@ struct scoutfs_net_connection { struct super_block *sb; scoutfs_net_notify_t notify_up; scoutfs_net_notify_t notify_down; + size_t info_size; scoutfs_net_request_t *req_funcs; spinlock_t lock; @@ -108,6 +109,8 @@ struct scoutfs_net_connection { /* message_recv proc_work also executes in the conn workq */ struct scoutfs_tseq_entry tseq_entry; + + u8 info[0] __aligned(sizeof(u64)); }; /* listening and their accepting sockets have a fixed locking order */ @@ -418,7 +421,7 @@ static void saw_valid_greeting(struct scoutfs_net_connection *conn, u64 node_id) conn->valid_greeting = 1; conn->node_id = node_id; if (conn->notify_up) - conn->notify_up(sb, conn); + conn->notify_up(sb, conn, conn->info, node_id); list_splice_tail_init(&conn->resend_queue, &conn->send_queue); queue_work(conn->workq, &conn->send_work); @@ -947,7 +950,9 @@ static void scoutfs_net_listen_worker(struct work_struct *work) break; /* inherit accepted request funcs from listening conn */ - acc_conn = scoutfs_net_alloc_conn(sb, NULL, NULL, + acc_conn = scoutfs_net_alloc_conn(sb, conn->notify_up, + conn->notify_down, + conn->info_size, conn->req_funcs, "accepted"); if (!acc_conn) { sock_release(acc_sock); @@ -1133,7 +1138,7 @@ static void scoutfs_net_shutdown_worker(struct work_struct *work) /* tell the caller that the connection is down */ if (conn->notify_down) - conn->notify_down(sb, conn); + conn->notify_down(sb, conn, conn->info, conn->node_id); /* accepted conns are destroyed */ if (conn->listening_conn) { @@ -1147,16 +1152,29 @@ static void scoutfs_net_shutdown_worker(struct work_struct *work) trace_scoutfs_net_shutdown_work_exit(sb, 0, 0); } +/* + * Accepted connections inherit the callbacks from their listening + * connection. + * + * notify_up is called once a valid greeting is received. node_id is + * non-zero on accepted sockets once they've seen a valid greeting. + * Connected and listening connections have a node_id of 0. + * + * notify_down is always called as connections are shut down. It can be + * called without notify_up ever being called. The node_id is only + * non-zero for accepted connections. + */ struct scoutfs_net_connection * scoutfs_net_alloc_conn(struct super_block *sb, scoutfs_net_notify_t notify_up, - scoutfs_net_notify_t notify_down, + scoutfs_net_notify_t notify_down, size_t info_size, scoutfs_net_request_t *req_funcs, char *name_suffix) { struct net_info *ninf = SCOUTFS_SB(sb)->net_info; struct scoutfs_net_connection *conn; - conn = kzalloc(sizeof(struct scoutfs_net_connection), GFP_NOFS); + conn = kzalloc(offsetof(struct scoutfs_net_connection, + info[info_size]), GFP_NOFS); if (!conn) return NULL; @@ -1171,6 +1189,7 @@ scoutfs_net_alloc_conn(struct super_block *sb, conn->sb = sb; conn->notify_up = notify_up; conn->notify_down = notify_down; + conn->info_size = info_size; conn->req_funcs = req_funcs; spin_lock_init(&conn->lock); init_waitqueue_head(&conn->waitq); diff --git a/kmod/src/net.h b/kmod/src/net.h index 7b7e0375..88fe3a1a 100644 --- a/kmod/src/net.h +++ b/kmod/src/net.h @@ -20,12 +20,13 @@ typedef int (*scoutfs_net_response_t)(struct super_block *sb, int error, void *data); typedef void (*scoutfs_net_notify_t)(struct super_block *sb, - struct scoutfs_net_connection *conn); + struct scoutfs_net_connection *conn, + void *info, u64 node_id); struct scoutfs_net_connection * scoutfs_net_alloc_conn(struct super_block *sb, scoutfs_net_notify_t notify_up, - scoutfs_net_notify_t notify_down, + scoutfs_net_notify_t notify_down, size_t info_size, scoutfs_net_request_t *req_funcs, char *name_suffix); int scoutfs_net_connect(struct super_block *sb, struct scoutfs_net_connection *conn, diff --git a/kmod/src/server.c b/kmod/src/server.c index f1d4962b..8f8eda8b 100644 --- a/kmod/src/server.c +++ b/kmod/src/server.c @@ -73,11 +73,21 @@ struct server_info { /* server tracks pending frees to be applied during commit */ struct rw_semaphore alloc_rwsem; struct list_head pending_frees; + + struct list_head clients; }; #define DECLARE_SERVER_INFO(sb, name) \ struct server_info *name = SCOUTFS_SB(sb)->server_info +/* + * The server tracks each connected client. + */ +struct server_client_info { + u64 node_id; + struct list_head head; +}; + struct commit_waiter { struct completion comp; struct llist_node node; @@ -1153,13 +1163,37 @@ static scoutfs_net_request_t server_req_funcs[] = { [SCOUTFS_NET_CMD_STATFS] = server_statfs, }; -static void server_notify_down(struct super_block *sb, - struct scoutfs_net_connection *conn) +static void server_notify_up(struct super_block *sb, + struct scoutfs_net_connection *conn, + void *info, u64 node_id) { + struct server_client_info *sci = info; DECLARE_SERVER_INFO(sb, server); - shutdown_server(server); + if (node_id != 0) { + sci->node_id = node_id; + spin_lock(&server->lock); + list_add_tail(&sci->head, &server->clients); + spin_unlock(&server->lock); + } } + +static void server_notify_down(struct super_block *sb, + struct scoutfs_net_connection *conn, + void *info, u64 node_id) +{ + struct server_client_info *sci = info; + DECLARE_SERVER_INFO(sb, server); + + if (node_id != 0) { + spin_lock(&server->lock); + list_del(&sci->head); + spin_unlock(&server->lock); + } else { + shutdown_server(server); + } +} + /* * This work is always running or has a delayed timer set while a super * is mounted. It tries to grab the lock to become the server. If it @@ -1193,7 +1227,8 @@ static void scoutfs_server_worker(struct work_struct *work) if (ret) goto out; - conn = scoutfs_net_alloc_conn(sb, NULL, server_notify_down, + conn = scoutfs_net_alloc_conn(sb, server_notify_up, server_notify_down, + sizeof(struct server_client_info), server_req_funcs, "server"); if (!conn) { ret = -ENOMEM; @@ -1302,6 +1337,7 @@ int scoutfs_server_setup(struct super_block *sb) INIT_LIST_HEAD(&server->pending_seqs); init_rwsem(&server->alloc_rwsem); INIT_LIST_HEAD(&server->pending_frees); + INIT_LIST_HEAD(&server->clients); server->wq = alloc_workqueue("scoutfs_server", WQ_NON_REENTRANT, 0); if (!server->wq) {