/* * KTCPVS An implementation of the TCP Virtual Server daemon inside * kernel for the LINUX operating system. KTCPVS can be used * to build a moderately scalable and highly available server * based on a cluster of servers, with more flexibility. * * Version: $Id$ * * Authors: Wensong Zhang * * 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 #include #include #include #include #include #ifdef CONFIG_KMOD #include #endif #include #include /* just for fixing implicit declaration of function `local_bh_disable', I don't know why */ #include "tcp_vs.h" /* * KTCPVS scheduler list */ LIST_HEAD(tcp_vs_schedulers); /* * FIXME: the scheduler lookup, insert, remove are all called through * /proc fs and the /proc fs uses the global lock (lock_kernel), so * there is no need to use the __tcp_vs_sched_lock here!!!!!! */ rwlock_t __tcp_vs_sched_lock = RW_LOCK_UNLOCKED; /* * Register a scheduler in the scheduler list */ int register_tcp_vs_scheduler(struct tcp_vs_scheduler *scheduler) { if (!scheduler) { TCP_VS_ERR("register_tcp_vs_scheduler(): NULL arg\n"); return -EINVAL; } if (!scheduler->name) { TCP_VS_ERR("register_tcp_vs_scheduler(): NULL scheduler_name\n"); return -EINVAL; } write_lock_bh(&__tcp_vs_sched_lock); if (scheduler->n_list.next != &scheduler->n_list) { TCP_VS_ERR("register_tcp_vs_scheduler(): scheduler already linked\n"); return -EINVAL; } /* * Add it into the d-linked scheduler list */ list_add(&scheduler->n_list, &tcp_vs_schedulers); write_unlock_bh(&__tcp_vs_sched_lock); MOD_INC_USE_COUNT; return 0; } /* * Unregister a scheduler in the scheduler list */ int unregister_tcp_vs_scheduler(struct tcp_vs_scheduler *scheduler) { if (!scheduler) { TCP_VS_ERR( "unregister_tcp_vs_scheduler(): NULL arg\n"); return -EINVAL; } /* * Only allow unregistration if it is not referenced */ if (atomic_read(&scheduler->refcnt)) { TCP_VS_ERR("unregister_tcp_vs_scheduler(): is in use by %d guys. failed\n", atomic_read(&scheduler->refcnt)); return -EINVAL; } write_lock_bh(&__tcp_vs_sched_lock); if (scheduler->n_list.next == &scheduler->n_list) { TCP_VS_ERR("unregister_tcp_vs_scheduler(): scheduler is not in the list. failed\n"); return -EINVAL; } /* * Removed it from the d-linked scheduler list */ list_del(&scheduler->n_list); write_unlock_bh(&__tcp_vs_sched_lock); MOD_DEC_USE_COUNT; return 0; } /* * Bind a service with a scheduler */ int tcp_vs_bind_scheduler(struct tcp_vs *vs, struct tcp_vs_scheduler *scheduler) { if (vs == NULL) { TCP_VS_ERR("tcp_vs_bind_scheduler(): vs arg NULL\n"); return -EINVAL; } if (scheduler == NULL) { TCP_VS_ERR("tcp_vs_bind_scheduler(): scheduler arg NULL\n"); return -EINVAL; } vs->scheduler = scheduler; atomic_inc(&scheduler->refcnt); if(scheduler->init_vs) if(scheduler->init_vs(vs) != 0) { TCP_VS_ERR("tcp_vs_bind_scheduler(): init error\n"); write_unlock_bh(&__tcp_vs_sched_lock); return -EINVAL; } return 0; } /* * Unbind a service with its scheduler */ int tcp_vs_unbind_scheduler(struct tcp_vs *vs) { struct tcp_vs_scheduler *sched; if (vs == NULL) { TCP_VS_ERR("tcp_vs_unbind_scheduler(): vs arg NULL\n"); return -EINVAL; } sched = vs->scheduler; if (sched == NULL) { TCP_VS_ERR("tcp_vs_unbind_scheduler(): vs isn't bound\n"); return -EINVAL; } if(sched->done_vs) if(sched->done_vs(vs) != 0) { TCP_VS_ERR("tcp_vs_unbind_scheduler(): done error\n"); write_unlock_bh(&__tcp_vs_sched_lock); return -EINVAL; } atomic_dec(&sched->refcnt); vs->scheduler = NULL; return 0; } /* * Get scheduler in the scheduler list by name */ struct tcp_vs_scheduler * tcp_vs_sched_getbyname(const char *sched_name) { struct tcp_vs_scheduler *sched; struct list_head *l, *e; TCP_VS_DBG("tcp_vs_sched_getbyname(): sched_name \"%s\"\n", sched_name); read_lock_bh(&__tcp_vs_sched_lock); l = &tcp_vs_schedulers; for (e=l->next; e!=l; e=e->next) { sched = list_entry(e, struct tcp_vs_scheduler, n_list); if (strcmp(sched_name, sched->name)==0) { /* HIT */ atomic_inc(&sched->refcnt); read_unlock_bh(&__tcp_vs_sched_lock); return sched; } } read_unlock_bh(&__tcp_vs_sched_lock); return NULL; } /* * Lookup scheduler and try to load it if it doesn't exist */ struct tcp_vs_scheduler * tcp_vs_get_scheduler(const char *sched_name) { struct tcp_vs_scheduler *sched; /* * Search for the scheduler by sched_name */ sched = tcp_vs_sched_getbyname(sched_name); /* * If scheduler not found, load the module and search again */ if (sched == NULL) { char module_name[KTCPVS_SCHEDNAME_MAXLEN]; sprintf(module_name,"tcp_vs_%s",sched_name); #ifdef CONFIG_KMOD request_module(module_name); #endif /* CONFIG_KMOD */ sched = tcp_vs_sched_getbyname(sched_name); } return sched; } void tcp_vs_put_scheduler(struct tcp_vs_scheduler *sched) { atomic_dec(&sched->refcnt); }