2005-04-17 02:20:36 +04:00
/*
* IPVS An implementation of the IP virtual server support for the
* LINUX operating system . IPVS is now implemented as a module
* over the Netfilter framework . IPVS can be used to build a
* high - performance and highly available server based on a
* cluster of servers .
*
* Version : $ Id : ip_vs_sched . c , v 1.13 2003 / 05 / 10 03 : 05 : 23 wensong Exp $
*
* Authors : Wensong Zhang < wensong @ linuxvirtualserver . org >
* Peter Kese < peter . kese @ ijs . si >
*
* 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 .
*
* Changes :
*
*/
# include <linux/module.h>
# include <linux/spinlock.h>
2006-01-05 23:14:43 +03:00
# include <linux/interrupt.h>
2005-04-17 02:20:36 +04:00
# include <asm/string.h>
# include <linux/kmod.h>
# include <net/ip_vs.h>
/*
* IPVS scheduler list
*/
static LIST_HEAD ( ip_vs_schedulers ) ;
/* lock for service table */
static DEFINE_RWLOCK ( __ip_vs_sched_lock ) ;
/*
* Bind a service with a scheduler
*/
int ip_vs_bind_scheduler ( struct ip_vs_service * svc ,
struct ip_vs_scheduler * scheduler )
{
int ret ;
if ( svc = = NULL ) {
IP_VS_ERR ( " ip_vs_bind_scheduler(): svc arg NULL \n " ) ;
return - EINVAL ;
}
if ( scheduler = = NULL ) {
IP_VS_ERR ( " ip_vs_bind_scheduler(): scheduler arg NULL \n " ) ;
return - EINVAL ;
}
svc - > scheduler = scheduler ;
if ( scheduler - > init_service ) {
ret = scheduler - > init_service ( svc ) ;
if ( ret ) {
IP_VS_ERR ( " ip_vs_bind_scheduler(): init error \n " ) ;
return ret ;
}
}
return 0 ;
}
/*
* Unbind a service with its scheduler
*/
int ip_vs_unbind_scheduler ( struct ip_vs_service * svc )
{
struct ip_vs_scheduler * sched ;
if ( svc = = NULL ) {
IP_VS_ERR ( " ip_vs_unbind_scheduler(): svc arg NULL \n " ) ;
return - EINVAL ;
}
sched = svc - > scheduler ;
if ( sched = = NULL ) {
IP_VS_ERR ( " ip_vs_unbind_scheduler(): svc isn't bound \n " ) ;
return - EINVAL ;
}
if ( sched - > done_service ) {
if ( sched - > done_service ( svc ) ! = 0 ) {
IP_VS_ERR ( " ip_vs_unbind_scheduler(): done error \n " ) ;
return - EINVAL ;
}
}
svc - > scheduler = NULL ;
return 0 ;
}
/*
* Get scheduler in the scheduler list by name
*/
static struct ip_vs_scheduler * ip_vs_sched_getbyname ( const char * sched_name )
{
struct ip_vs_scheduler * sched ;
IP_VS_DBG ( 2 , " ip_vs_sched_getbyname(): sched_name \" %s \" \n " ,
sched_name ) ;
read_lock_bh ( & __ip_vs_sched_lock ) ;
list_for_each_entry ( sched , & ip_vs_schedulers , n_list ) {
/*
* Test and get the modules atomically
*/
if ( sched - > module & & ! try_module_get ( sched - > module ) ) {
/*
* This scheduler is just deleted
*/
continue ;
}
if ( strcmp ( sched_name , sched - > name ) = = 0 ) {
/* HIT */
read_unlock_bh ( & __ip_vs_sched_lock ) ;
return sched ;
}
if ( sched - > module )
module_put ( sched - > module ) ;
}
read_unlock_bh ( & __ip_vs_sched_lock ) ;
return NULL ;
}
/*
* Lookup scheduler and try to load it if it doesn ' t exist
*/
struct ip_vs_scheduler * ip_vs_scheduler_get ( const char * sched_name )
{
struct ip_vs_scheduler * sched ;
/*
* Search for the scheduler by sched_name
*/
sched = ip_vs_sched_getbyname ( sched_name ) ;
/*
* If scheduler not found , load the module and search again
*/
if ( sched = = NULL ) {
request_module ( " ip_vs_%s " , sched_name ) ;
sched = ip_vs_sched_getbyname ( sched_name ) ;
}
return sched ;
}
void ip_vs_scheduler_put ( struct ip_vs_scheduler * scheduler )
{
if ( scheduler - > module )
module_put ( scheduler - > module ) ;
}
/*
* Register a scheduler in the scheduler list
*/
int register_ip_vs_scheduler ( struct ip_vs_scheduler * scheduler )
{
struct ip_vs_scheduler * sched ;
if ( ! scheduler ) {
IP_VS_ERR ( " register_ip_vs_scheduler(): NULL arg \n " ) ;
return - EINVAL ;
}
if ( ! scheduler - > name ) {
IP_VS_ERR ( " register_ip_vs_scheduler(): NULL scheduler_name \n " ) ;
return - EINVAL ;
}
/* increase the module use count */
ip_vs_use_count_inc ( ) ;
/*
* Make sure that the scheduler with this name doesn ' t exist
* in the scheduler list .
*/
sched = ip_vs_sched_getbyname ( scheduler - > name ) ;
if ( sched ) {
ip_vs_scheduler_put ( sched ) ;
ip_vs_use_count_dec ( ) ;
IP_VS_ERR ( " register_ip_vs_scheduler(): [%s] scheduler "
" already existed in the system \n " , scheduler - > name ) ;
return - EINVAL ;
}
write_lock_bh ( & __ip_vs_sched_lock ) ;
if ( scheduler - > n_list . next ! = & scheduler - > n_list ) {
write_unlock_bh ( & __ip_vs_sched_lock ) ;
ip_vs_use_count_dec ( ) ;
IP_VS_ERR ( " register_ip_vs_scheduler(): [%s] scheduler "
" already linked \n " , scheduler - > name ) ;
return - EINVAL ;
}
/*
* Add it into the d - linked scheduler list
*/
list_add ( & scheduler - > n_list , & ip_vs_schedulers ) ;
write_unlock_bh ( & __ip_vs_sched_lock ) ;
IP_VS_INFO ( " [%s] scheduler registered. \n " , scheduler - > name ) ;
return 0 ;
}
/*
* Unregister a scheduler from the scheduler list
*/
int unregister_ip_vs_scheduler ( struct ip_vs_scheduler * scheduler )
{
if ( ! scheduler ) {
IP_VS_ERR ( " unregister_ip_vs_scheduler(): NULL arg \n " ) ;
return - EINVAL ;
}
write_lock_bh ( & __ip_vs_sched_lock ) ;
if ( scheduler - > n_list . next = = & scheduler - > n_list ) {
write_unlock_bh ( & __ip_vs_sched_lock ) ;
IP_VS_ERR ( " unregister_ip_vs_scheduler(): [%s] scheduler "
" is not in the list. failed \n " , scheduler - > name ) ;
return - EINVAL ;
}
/*
* Remove it from the d - linked scheduler list
*/
list_del ( & scheduler - > n_list ) ;
write_unlock_bh ( & __ip_vs_sched_lock ) ;
/* decrease the module use count */
ip_vs_use_count_dec ( ) ;
IP_VS_INFO ( " [%s] scheduler unregistered. \n " , scheduler - > name ) ;
return 0 ;
}