server.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 503 行

C
503
字号
/* server.c: AFS server record management * * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#include <linux/sched.h>#include <linux/slab.h>#include <rxrpc/peer.h>#include <rxrpc/connection.h>#include "volume.h"#include "cell.h"#include "server.h"#include "transport.h"#include "vlclient.h"#include "kafstimod.h"#include "internal.h"spinlock_t afs_server_peer_lock = SPIN_LOCK_UNLOCKED;#define FS_SERVICE_ID		1	/* AFS Volume Location Service ID */#define VL_SERVICE_ID		52	/* AFS Volume Location Service ID */static void __afs_server_timeout(struct afs_timer *timer){	struct afs_server *server =		list_entry(timer, struct afs_server, timeout);	_debug("SERVER TIMEOUT [%p{u=%d}]",	       server, atomic_read(&server->usage));	afs_server_do_timeout(server);}static const struct afs_timer_ops afs_server_timer_ops = {	.timed_out	= __afs_server_timeout,};/*****************************************************************************//* * lookup a server record in a cell * - TODO: search the cell's server list */int afs_server_lookup(struct afs_cell *cell, const struct in_addr *addr,		      struct afs_server **_server){	struct afs_server *server, *active, *zombie;	int loop;	_enter("%p,%08x,", cell, ntohl(addr->s_addr));	/* allocate and initialise a server record */	server = kmalloc(sizeof(struct afs_server), GFP_KERNEL);	if (!server) {		_leave(" = -ENOMEM");		return -ENOMEM;	}	memset(server, 0, sizeof(struct afs_server));	atomic_set(&server->usage, 1);	INIT_LIST_HEAD(&server->link);	init_rwsem(&server->sem);	INIT_LIST_HEAD(&server->fs_callq);	spin_lock_init(&server->fs_lock);	INIT_LIST_HEAD(&server->cb_promises);	spin_lock_init(&server->cb_lock);	for (loop = 0; loop < AFS_SERVER_CONN_LIST_SIZE; loop++)		server->fs_conn_cnt[loop] = 4;	memcpy(&server->addr, addr, sizeof(struct in_addr));	server->addr.s_addr = addr->s_addr;	afs_timer_init(&server->timeout, &afs_server_timer_ops);	/* add to the cell */	write_lock(&cell->sv_lock);	/* check the active list */	list_for_each_entry(active, &cell->sv_list, link) {		if (active->addr.s_addr == addr->s_addr)			goto use_active_server;	}	/* check the inactive list */	spin_lock(&cell->sv_gylock);	list_for_each_entry(zombie, &cell->sv_graveyard, link) {		if (zombie->addr.s_addr == addr->s_addr)			goto resurrect_server;	}	spin_unlock(&cell->sv_gylock);	afs_get_cell(cell);	server->cell = cell;	list_add_tail(&server->link, &cell->sv_list);	write_unlock(&cell->sv_lock);	*_server = server;	_leave(" = 0 (%p)", server);	return 0;	/* found a matching active server */ use_active_server:	_debug("active server");	afs_get_server(active);	write_unlock(&cell->sv_lock);	kfree(server);	*_server = active;	_leave(" = 0 (%p)", active);	return 0;	/* found a matching server in the graveyard, so resurrect it and	 * dispose of the new record */ resurrect_server:	_debug("resurrecting server");	list_del(&zombie->link);	list_add_tail(&zombie->link, &cell->sv_list);	afs_get_server(zombie);	afs_kafstimod_del_timer(&zombie->timeout);	spin_unlock(&cell->sv_gylock);	write_unlock(&cell->sv_lock);	kfree(server);	*_server = zombie;	_leave(" = 0 (%p)", zombie);	return 0;} /* end afs_server_lookup() *//*****************************************************************************//* * destroy a server record * - removes from the cell list */void afs_put_server(struct afs_server *server){	struct afs_cell *cell;	if (!server)		return;	_enter("%p", server);	cell = server->cell;	/* sanity check */	BUG_ON(atomic_read(&server->usage) <= 0);	/* to prevent a race, the decrement and the dequeue must be effectively	 * atomic */	write_lock(&cell->sv_lock);	if (likely(!atomic_dec_and_test(&server->usage))) {		write_unlock(&cell->sv_lock);		_leave("");		return;	}	spin_lock(&cell->sv_gylock);	list_del(&server->link);	list_add_tail(&server->link, &cell->sv_graveyard);	/* time out in 10 secs */	afs_kafstimod_add_timer(&server->timeout, 10 * HZ);	spin_unlock(&cell->sv_gylock);	write_unlock(&cell->sv_lock);	_leave(" [killed]");} /* end afs_put_server() *//*****************************************************************************//* * timeout server record * - removes from the cell's graveyard if the usage count is zero */void afs_server_do_timeout(struct afs_server *server){	struct rxrpc_peer *peer;	struct afs_cell *cell;	int loop;	_enter("%p", server);	cell = server->cell;	BUG_ON(atomic_read(&server->usage) < 0);	/* remove from graveyard if still dead */	spin_lock(&cell->vl_gylock);	if (atomic_read(&server->usage) == 0)		list_del_init(&server->link);	else		server = NULL;	spin_unlock(&cell->vl_gylock);	if (!server) {		_leave("");		return; /* resurrected */	}	/* we can now destroy it properly */	afs_put_cell(cell);	/* uncross-point the structs under a global lock */	spin_lock(&afs_server_peer_lock);	peer = server->peer;	if (peer) {		server->peer = NULL;		peer->user = NULL;	}	spin_unlock(&afs_server_peer_lock);	/* finish cleaning up the server */	for (loop = AFS_SERVER_CONN_LIST_SIZE - 1; loop >= 0; loop--)		if (server->fs_conn[loop])			rxrpc_put_connection(server->fs_conn[loop]);	if (server->vlserver)		rxrpc_put_connection(server->vlserver);	kfree(server);	_leave(" [destroyed]");} /* end afs_server_do_timeout() *//*****************************************************************************//* * get a callslot on a connection to the fileserver on the specified server */int afs_server_request_callslot(struct afs_server *server,				struct afs_server_callslot *callslot){	struct afs_server_callslot *pcallslot;	struct rxrpc_connection *conn;	int nconn, ret;	_enter("%p,",server);	INIT_LIST_HEAD(&callslot->link);	callslot->task = current;	callslot->conn = NULL;	callslot->nconn = -1;	callslot->ready = 0;	ret = 0;	conn = NULL;	/* get hold of a callslot first */	spin_lock(&server->fs_lock);	/* resurrect the server if it's death timeout has expired */	if (server->fs_state) {		if (time_before(jiffies, server->fs_dead_jif)) {			ret = server->fs_state;			spin_unlock(&server->fs_lock);			_leave(" = %d [still dead]", ret);			return ret;		}		server->fs_state = 0;	}	/* try and find a connection that has spare callslots */	for (nconn = 0; nconn < AFS_SERVER_CONN_LIST_SIZE; nconn++) {		if (server->fs_conn_cnt[nconn] > 0) {			server->fs_conn_cnt[nconn]--;			spin_unlock(&server->fs_lock);			callslot->nconn = nconn;			goto obtained_slot;		}	}	/* none were available - wait interruptibly for one to become	 * available */	set_current_state(TASK_INTERRUPTIBLE);	list_add_tail(&callslot->link, &server->fs_callq);	spin_unlock(&server->fs_lock);	while (!callslot->ready && !signal_pending(current)) {		schedule();		set_current_state(TASK_INTERRUPTIBLE);	}	set_current_state(TASK_RUNNING);	/* even if we were interrupted we may still be queued */	if (!callslot->ready) {		spin_lock(&server->fs_lock);		list_del_init(&callslot->link);		spin_unlock(&server->fs_lock);	}	nconn = callslot->nconn;	/* if interrupted, we must release any slot we also got before	 * returning an error */	if (signal_pending(current)) {		ret = -EINTR;		goto error_release;	}	/* if we were woken up with an error, then pass that error back to the	 * called */	if (nconn < 0) {		_leave(" = %d", callslot->errno);		return callslot->errno;	}	/* were we given a connection directly? */	if (callslot->conn) {		/* yes - use it */		_leave(" = 0 (nc=%d)", nconn);		return 0;	}	/* got a callslot, but no connection */ obtained_slot:	/* need to get hold of the RxRPC connection */	down_write(&server->sem);	/* quick check to see if there's an outstanding error */	ret = server->fs_state;	if (ret)		goto error_release_upw;	if (server->fs_conn[nconn]) {		/* reuse an existing connection */		rxrpc_get_connection(server->fs_conn[nconn]);		callslot->conn = server->fs_conn[nconn];	}	else {		/* create a new connection */		ret = rxrpc_create_connection(afs_transport,					      htons(7000),					      server->addr.s_addr,					      FS_SERVICE_ID,					      NULL,					      &server->fs_conn[nconn]);		if (ret < 0)			goto error_release_upw;		callslot->conn = server->fs_conn[0];		rxrpc_get_connection(callslot->conn);	}	up_write(&server->sem); 	_leave(" = 0");	return 0;	/* handle an error occurring */ error_release_upw:	up_write(&server->sem); error_release:	/* either release the callslot or pass it along to another deserving	 * task */	spin_lock(&server->fs_lock);	if (nconn < 0) {		/* no callslot allocated */	}	else if (list_empty(&server->fs_callq)) {		/* no one waiting */		server->fs_conn_cnt[nconn]++;		spin_unlock(&server->fs_lock);	}	else {		/* someone's waiting - dequeue them and wake them up */		pcallslot = list_entry(server->fs_callq.next,				       struct afs_server_callslot, link);		list_del_init(&pcallslot->link);		pcallslot->errno = server->fs_state;		if (!pcallslot->errno) {			/* pass them out callslot details */			callslot->conn = xchg(&pcallslot->conn,					      callslot->conn);			pcallslot->nconn = nconn;			callslot->nconn = nconn = -1;		}		pcallslot->ready = 1;		wake_up_process(pcallslot->task);		spin_unlock(&server->fs_lock);	}	rxrpc_put_connection(callslot->conn);	callslot->conn = NULL;	_leave(" = %d", ret);	return ret;} /* end afs_server_request_callslot() *//*****************************************************************************//* * release a callslot back to the server * - transfers the RxRPC connection to the next pending callslot if possible */void afs_server_release_callslot(struct afs_server *server,				 struct afs_server_callslot *callslot){	struct afs_server_callslot *pcallslot;	_enter("{ad=%08x,cnt=%u},{%d}",	       ntohl(server->addr.s_addr),	       server->fs_conn_cnt[callslot->nconn],	       callslot->nconn);	BUG_ON(callslot->nconn < 0);	spin_lock(&server->fs_lock);	if (list_empty(&server->fs_callq)) {		/* no one waiting */		server->fs_conn_cnt[callslot->nconn]++;		spin_unlock(&server->fs_lock);	}	else {		/* someone's waiting - dequeue them and wake them up */		pcallslot = list_entry(server->fs_callq.next,				       struct afs_server_callslot, link);		list_del_init(&pcallslot->link);		pcallslot->errno = server->fs_state;		if (!pcallslot->errno) {			/* pass them out callslot details */			callslot->conn = xchg(&pcallslot->conn, callslot->conn);			pcallslot->nconn = callslot->nconn;			callslot->nconn = -1;		}		pcallslot->ready = 1;		wake_up_process(pcallslot->task);		spin_unlock(&server->fs_lock);	}	rxrpc_put_connection(callslot->conn);	_leave("");} /* end afs_server_release_callslot() *//*****************************************************************************//* * get a handle to a connection to the vlserver (volume location) on the * specified server */int afs_server_get_vlconn(struct afs_server *server,			  struct rxrpc_connection **_conn){	struct rxrpc_connection *conn;	int ret;	_enter("%p,", server);	ret = 0;	conn = NULL;	down_read(&server->sem);	if (server->vlserver) {		/* reuse an existing connection */		rxrpc_get_connection(server->vlserver);		conn = server->vlserver;		up_read(&server->sem);	}	else {		/* create a new connection */		up_read(&server->sem);		down_write(&server->sem);		if (!server->vlserver) {			ret = rxrpc_create_connection(afs_transport,						      htons(7003),						      server->addr.s_addr,						      VL_SERVICE_ID,						      NULL,						      &server->vlserver);		}		if (ret == 0) {			rxrpc_get_connection(server->vlserver);			conn = server->vlserver;		}		up_write(&server->sem);	}	*_conn = conn;	_leave(" = %d", ret);	return ret;} /* end afs_server_get_vlconn() */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?