/* * 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: tcp_vs_sched.c,v 1.6 2003/05/23 02:08:34 wensong Exp $ * * 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 #include //#include /* for local_bh_* */ #include #ifdef CONFIG_KMOD #include #endif #include "tcp_vs.h" /* * KTCPVS scheduler list */ static 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!!!!!! */ static 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; } if (scheduler->n_list.next != &scheduler->n_list) { TCP_VS_ERR ("register_tcp_vs_scheduler(): scheduler already linked\n"); return -EINVAL; } //MOD_INC_USE_COUNT; write_lock_bh(&__tcp_vs_sched_lock); /* * Add it into the d-linked scheduler list */ list_add(&scheduler->n_list, &tcp_vs_schedulers); write_unlock_bh(&__tcp_vs_sched_lock); TCP_VS_INFO("[%s] scheduler registered.\n", scheduler->name); 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; } if (list_empty(&scheduler->n_list)) { TCP_VS_ERR ("unregister_tcp_vs_scheduler(): scheduler is not in the list. failed\n"); return -EINVAL; } write_lock_bh(&__tcp_vs_sched_lock); /* * Removed it from the d-linked scheduler list */ list_del(&scheduler->n_list); write_unlock_bh(&__tcp_vs_sched_lock); //MOD_DEC_USE_COUNT; TCP_VS_INFO("[%s] scheduler unregistered.\n", scheduler->name); return 0; } /* * Bind a service with a scheduler */ int tcp_vs_bind_scheduler(struct tcp_vs_service *svc, struct tcp_vs_scheduler *sched) { if (svc == NULL) { TCP_VS_ERR("tcp_vs_bind_scheduler(): vs arg NULL\n"); return -EINVAL; } if (sched == NULL) { TCP_VS_ERR ("tcp_vs_bind_scheduler(): scheduler arg NULL\n"); return -EINVAL; } /* if (sched->module) * __MOD_INC_USE_COUNT(sched->module); */ svc->scheduler = sched; if (sched->init_service && sched->init_service(svc) != 0) { TCP_VS_ERR("tcp_vs_bind_scheduler(): init error\n"); /* if (sched->module) * __MOD_DEC_USE_COUNT(sched->module); */ return -EINVAL; } return 0; } /* * Unbind a service with its scheduler */ int tcp_vs_unbind_scheduler(struct tcp_vs_service *svc) { struct tcp_vs_scheduler *sched; if (svc == NULL) { TCP_VS_ERR("tcp_vs_unbind_scheduler(): svc arg NULL\n"); return -EINVAL; } sched = svc->scheduler; if (sched == NULL) { TCP_VS_ERR("tcp_vs_unbind_scheduler(): svc isn't bound\n"); return -EINVAL; } if (sched->done_service && sched->done_service(svc) != 0) { TCP_VS_ERR("tcp_vs_unbind_scheduler(): done error\n"); return -EINVAL; } svc->scheduler = NULL; /* if (sched->module) * __MOD_DEC_USE_COUNT(sched->module); */ return 0; } /* * Get scheduler in the scheduler list by name */ static 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(5, "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); /* Test and MOD_INC_USE_COUNT atomically */ //if (sched->module && !try_inc_mod_count(sched->module)) { //continue; //} if (!strcmp(sched_name, sched->name)) { /* HIT */ read_unlock_bh(&__tcp_vs_sched_lock); return sched; } /* if (sched->module) * __MOD_DEC_USE_COUNT(sched->module); */ } 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_scheduler_get(const char *sched_name) { struct tcp_vs_scheduler *sched; /* * Search for the scheduler by sched_name */ sched = tcp_vs_sched_getbyname(sched_name); #ifdef CONFIG_KMOD /* * If scheduler not found, load the module and search again */ if (sched == NULL) { char module_name[KTCPVS_SCHEDNAME_MAXLEN]; sprintf(module_name, "ktcpvs_%s", sched_name); request_module(module_name); sched = tcp_vs_sched_getbyname(sched_name); } #endif /* CONFIG_KMOD */ return sched; } /* void tcp_vs_scheduler_put(struct tcp_vs_scheduler *sched) { if (sched->module) __MOD_DEC_USE_COUNT(sched->module); } */