scoutfs: have server track connected clients

This extends the notify up and down calls to let the server keep track
of connected clients.

It adds the notion of per-connection info that is allocated for each
connection.  It's passed to the notification callbacks so that callers
can have per-client storage without having to manage allocations in the
callbacks.

It adds the node_id argument to the notification callbacks to indicate
if the call is for the listening socket itself or an accepted client
connection on that listening socket.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2018-07-30 16:43:06 -07:00
committed by Zach Brown
parent 746293987c
commit 0adbd7e439
4 changed files with 70 additions and 13 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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,

View File

@@ -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) {