diff options
author | David Sommerseth <davids@redhat.com> | 2011-04-11 16:36:56 +0200 |
---|---|---|
committer | David Sommerseth <davids@redhat.com> | 2011-04-11 16:36:56 +0200 |
commit | 508ffffbb3c48eeeb11eeab2bf971180fe4e1940 (patch) | |
tree | 2b8133c62a904fd400f0cef0a3e2abb12048149a | |
parent | abc7f912f66d41dd734a10900429d4cad9377da5 (diff) | |
download | python-ethtool-508ffffbb3c48eeeb11eeab2bf971180fe4e1940.tar.gz python-ethtool-508ffffbb3c48eeeb11eeab2bf971180fe4e1940.tar.xz python-ethtool-508ffffbb3c48eeeb11eeab2bf971180fe4e1940.zip |
Only open the NETLINK interface when needed
Do not open a NETLINK connection when loading the module, but rahter
open it when needed. In a case where multiple users needs the
connection, it will be shared and only closed when the last active
user is done.
Signed-off-by: David Sommerseth <davids@redhat.com>
-rw-r--r-- | python-ethtool/etherinfo.c | 97 | ||||
-rw-r--r-- | python-ethtool/etherinfo.h | 7 | ||||
-rw-r--r-- | python-ethtool/etherinfo_obj.c | 19 | ||||
-rw-r--r-- | python-ethtool/etherinfo_struct.h | 6 | ||||
-rw-r--r-- | python-ethtool/ethtool.c | 58 | ||||
-rw-r--r-- | setup.py | 6 |
6 files changed, 118 insertions, 75 deletions
diff --git a/python-ethtool/etherinfo.c b/python-ethtool/etherinfo.c index 5539cf7..42973ad 100644 --- a/python-ethtool/etherinfo.c +++ b/python-ethtool/etherinfo.c @@ -1,6 +1,6 @@ /* etherinfo.c - Retrieve ethernet interface info via NETLINK * - * Copyright (C) 2009-2010 Red Hat Inc. + * Copyright (C) 2009-2011 Red Hat Inc. * * David Sommerseth <davids@redhat.com> * Parts of this code is based on ideas and solutions in iproute2 @@ -27,9 +27,12 @@ #include <netlink/route/rtnl.h> #include <assert.h> #include <errno.h> +#include <pthread.h> #include "etherinfo_struct.h" #include "etherinfo.h" +pthread_mutex_t nlc_counter_mtx = PTHREAD_MUTEX_INITIALIZER; + /* * * Internal functions for working with struct etherinfo @@ -274,15 +277,25 @@ void dump_etherinfo(FILE *fp, struct etherinfo *ptr) * * @return Returns 1 on success, otherwise 0. */ -int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query) +int get_etherinfo(struct etherinfo_obj_data *data, nlQuery query) { struct nl_cache *link_cache; struct nl_cache *addr_cache; struct rtnl_addr *addr; struct rtnl_link *link; + struct etherinfo *ethinf = NULL; int ret = 0; - if( !ethinf || !nlc ) { + if( !data || !data->ethinfo ) { + return 0; + } + ethinf = data->ethinfo; + + /* Open a NETLINK connection on-the-fly */ + if( !open_netlink(data) ) { + PyErr_Format(PyExc_RuntimeError, + "Could not open a NETLINK connection for %s", + ethinf->device); return 0; } @@ -291,7 +304,7 @@ int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query * interface index if we have that */ if( ethinf->index < 0 ) { - link_cache = rtnl_link_alloc_cache(nlc); + link_cache = rtnl_link_alloc_cache(*data->nlc); ethinf->index = rtnl_link_name2i(link_cache, ethinf->device); if( ethinf->index < 0 ) { return 0; @@ -303,7 +316,7 @@ int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query switch( query ) { case NLQRY_LINK: /* Extract MAC/hardware address of the interface */ - link_cache = rtnl_link_alloc_cache(nlc); + link_cache = rtnl_link_alloc_cache(*data->nlc); link = rtnl_link_alloc(); rtnl_link_set_ifindex(link, ethinf->index); nl_cache_foreach_filter(link_cache, (struct nl_object *)link, callback_nl_link, ethinf); @@ -314,7 +327,7 @@ int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query case NLQRY_ADDR: /* Extract IP address information */ - addr_cache = rtnl_addr_alloc_cache(nlc); + addr_cache = rtnl_addr_alloc_cache(*data->nlc); addr = rtnl_addr_alloc(); rtnl_addr_set_ifindex(addr, ethinf->index); @@ -337,3 +350,75 @@ int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query return ret; } + +/** + * Connects to the NETLINK interface. This will be called + * for each etherinfo object being generated, and it will + * keep a separate file descriptor open for each object + * + * @param data etherinfo_obj_data structure + * + * @return Returns 1 on success, otherwise 0. + */ +int open_netlink(struct etherinfo_obj_data *data) +{ + if( !data ) { + return 0; + } + + /* Reuse already established NETLINK connection, if a connection exists */ + if( *data->nlc ) { + /* If this object has not used NETLINK earlier, tag it as a user */ + if( !data->nlc_active ) { + pthread_mutex_lock(&nlc_counter_mtx); + (*data->nlc_users)++; + pthread_mutex_unlock(&nlc_counter_mtx); + } + data->nlc_active = 1; + return 1; + } + + /* No earlier connections exists, establish a new one */ + *data->nlc = nl_handle_alloc(); + nl_connect(*data->nlc, NETLINK_ROUTE); + if( (*data->nlc != NULL) ) { + /* Tag this object as an active user */ + pthread_mutex_lock(&nlc_counter_mtx); + (*data->nlc_users)++; + pthread_mutex_unlock(&nlc_counter_mtx); + data->nlc_active = 1; + return 1; + } else { + return 0; + } +} + + +/** + * Closes the NETLINK connection. This should be called automatically whenever + * the corresponding etherinfo object is deleted. + * + * @param ptr Pointer to the pointer of struct nl_handle, which contains the NETLINK connection + */ +void close_netlink(struct etherinfo_obj_data *data) +{ + if( !data || !(*data->nlc) ) { + return; + } + + /* Untag this object as a NETLINK user */ + data->nlc_active = 0; + pthread_mutex_lock(&nlc_counter_mtx); + (*data->nlc_users)--; + pthread_mutex_unlock(&nlc_counter_mtx); + + /* Don't close the connection if there are more users */ + if( *data->nlc_users > 0) { + return; + } + + /* Close NETLINK connection */ + nl_close(*data->nlc); + nl_handle_destroy(*data->nlc); + *data->nlc = NULL; +} diff --git a/python-ethtool/etherinfo.h b/python-ethtool/etherinfo.h index db368db..5e1056a 100644 --- a/python-ethtool/etherinfo.h +++ b/python-ethtool/etherinfo.h @@ -1,6 +1,6 @@ /* etherinfo.h - Retrieve ethernet interface info via NETLINK * - * Copyright (C) 2009-2010 Red Hat Inc. + * Copyright (C) 2009-2011 Red Hat Inc. * * David Sommerseth <davids@redhat.com> * @@ -26,10 +26,13 @@ typedef enum {NLQRY_LINK, NLQRY_ADDR} nlQuery; /**< Supported query types in the etherinfo code */ -int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query); +int get_etherinfo(struct etherinfo_obj_data *data, nlQuery query); void free_etherinfo(struct etherinfo *ptr); void dump_etherinfo(FILE *, struct etherinfo *); void free_ipv6addresses(struct ipv6address *ptr); +int open_netlink(struct etherinfo_obj_data *); +void close_netlink(struct etherinfo_obj_data *); + #endif diff --git a/python-ethtool/etherinfo_obj.c b/python-ethtool/etherinfo_obj.c index e1e03fe..cf90601 100644 --- a/python-ethtool/etherinfo_obj.c +++ b/python-ethtool/etherinfo_obj.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2010 Red Hat Inc. + * Copyright (C) 2009-2011 Red Hat Inc. * * David Sommerseth <davids@redhat.com> * @@ -40,6 +40,8 @@ extern PyTypeObject ethtool_etherinfoIPv6Type; void _ethtool_etherinfo_dealloc(etherinfo_py *self) { if( self->data ) { + close_netlink(self->data); + if( self->data->ethinfo ) { free_etherinfo(self->data->ethinfo); } @@ -110,20 +112,21 @@ PyObject *_ethtool_etherinfo_getter(etherinfo_py *self, PyObject *attr_o) if( strcmp(attr, "device") == 0 ) { ret = RETURN_STRING(self->data->ethinfo->device); } else if( strcmp(attr, "mac_address") == 0 ) { - get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_LINK); + get_etherinfo(self->data, NLQRY_LINK); ret = RETURN_STRING(self->data->ethinfo->hwaddress); } else if( strcmp(attr, "ipv4_address") == 0 ) { - get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_ADDR); + get_etherinfo(self->data, NLQRY_ADDR); ret = RETURN_STRING(self->data->ethinfo->ipv4_address); } else if( strcmp(attr, "ipv4_netmask") == 0 ) { - get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_ADDR); + get_etherinfo(self->data, NLQRY_ADDR); ret = PyInt_FromLong(self->data->ethinfo->ipv4_netmask); } else if( strcmp(attr, "ipv4_broadcast") == 0 ) { - get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_ADDR); + get_etherinfo(self->data, NLQRY_ADDR); ret = RETURN_STRING(self->data->ethinfo->ipv4_broadcast); } else { ret = PyObject_GenericGetAttr((PyObject *)self, attr_o); } + return ret; } @@ -160,8 +163,8 @@ PyObject *_ethtool_etherinfo_str(etherinfo_py *self) return NULL; } - get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_LINK); - get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_ADDR); + get_etherinfo(self->data, NLQRY_LINK); + get_etherinfo(self->data, NLQRY_ADDR); ret = PyString_FromFormat("Device %s:\n", self->data->ethinfo->device); if( self->data->ethinfo->hwaddress ) { @@ -223,7 +226,7 @@ PyObject * _ethtool_etherinfo_get_ipv6_addresses(etherinfo_py *self, PyObject *n return NULL; } - get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_ADDR); + get_etherinfo(self->data, NLQRY_ADDR); ipv6 = self->data->ethinfo->ipv6_addresses; ret = PyTuple_New(1); if( !ret ) { diff --git a/python-ethtool/etherinfo_struct.h b/python-ethtool/etherinfo_struct.h index 3ae037f..f294637 100644 --- a/python-ethtool/etherinfo_struct.h +++ b/python-ethtool/etherinfo_struct.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2010 Red Hat Inc. + * Copyright (C) 2009-2011 Red Hat Inc. * * David Sommerseth <davids@redhat.com> * @@ -58,7 +58,9 @@ struct ipv6address { * */ struct etherinfo_obj_data { - struct nl_handle *nlc; /**< Contains NETLINK connection info */ + struct nl_handle **nlc; /**< Contains NETLINK connection info (global) */ + unsigned int *nlc_users; /**< Resource counter for the NETLINK connection (global) */ + unsigned short nlc_active; /**< Is this instance using NETLINK? */ struct etherinfo *ethinfo; /**< Contains info about our current interface */ }; diff --git a/python-ethtool/ethtool.c b/python-ethtool/ethtool.c index 60e407c..b3fc65b 100644 --- a/python-ethtool/ethtool.c +++ b/python-ethtool/ethtool.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2010 Red Hat Inc. + * Copyright (C) 2008-2011 Red Hat Inc. * * Arnaldo Carvalho de Melo <acme@redhat.com> * David Sommerseth <davids@redhat.com> @@ -32,6 +32,7 @@ #include "etherinfo.h" static struct nl_handle *nlconnection = NULL; +unsigned int nlconnection_users = 0; /* How many NETLINK users are active? */ extern PyTypeObject ethtool_etherinfoType; extern PyTypeObject ethtool_etherinfoIPv6Type; @@ -314,7 +315,8 @@ static PyObject *get_interfaces_info(PyObject *self __unused, PyObject *args) { */ objdata->ethinfo->device = strdup(fetch_devs[i]); objdata->ethinfo->index = -1; - objdata->nlc = nlconnection; /* Global variable */ + objdata->nlc = &nlconnection; + objdata->nlc_users = &nlconnection_users; /* Instantiate a new etherinfo object with the device information */ ethinf_py = PyCObject_FromVoidPtr(objdata, NULL); @@ -979,52 +981,6 @@ static struct PyMethodDef PyEthModuleMethods[] = { }; -/** - * Connects to the NETLINK interface. This should only be - * called once as part of the main ethtool module init. - * - * @param nlc Structure which keeps the NETLINK connection handle (struct nl_handle) - * - * @return Returns 1 on success, otherwise 0. - */ -int open_netlink(struct nl_handle **nlc) -{ - if( *nlc ) { - return 0; - } - - *nlc = nl_handle_alloc(); - nl_connect(*nlc, NETLINK_ROUTE); - return (*nlc != NULL); -} - - -/** - * Closes the NETLINK connection. This should be called automatically whenever - * the ethtool module is unloaded from Python. - * - * @param ptr Pointer to the pointer of struct nl_handle, which contains the NETLINK connection - */ -void close_netlink(void **ptr) -{ - struct nl_handle *nlc; - - if( !ptr && !*ptr ) { - return; - } - - nlc = (struct nl_handle *) *ptr; - if( !nlc ) { - return; - } - - /* Close NETLINK connection */ - nl_close(nlc); - nl_handle_destroy(nlc); - *ptr = NULL; /* reset the pointers pointer address */ -} - - PyMODINIT_FUNC initethtool(void) { PyObject *m; @@ -1042,12 +998,6 @@ PyMODINIT_FUNC initethtool(void) Py_INCREF(ðtool_etherinfoIPv6Type); PyModule_AddObject(m, "etherinfo_ipv6addr", (PyObject *)ðtool_etherinfoIPv6Type); - // Prepare an internal netlink connection object - if( open_netlink(&nlconnection) ) { - PyModule_AddObject(m, "__nlconnection", - PyCObject_FromVoidPtr(&nlconnection, close_netlink)); - } - // Setup constants PyModule_AddIntConstant(m, "IFF_UP", IFF_UP); /* Interface is up. */ PyModule_AddIntConstant(m, "IFF_BROADCAST", IFF_BROADCAST); /* Broadcast address valid. */ @@ -4,7 +4,7 @@ from distutils.core import setup, Extension import commands import sys -version = '0.6' +version = '0.7' ethtool = Extension('ethtool', sources = ['python-ethtool/ethtool.c', @@ -53,8 +53,8 @@ libnl = pkgconfig('libnl-1') setup(name='ethtool', version=version, description='Python module to interface with ethtool', - author='Harald Hoyer & Arnaldo Carvalho de Melo', - author_email='acme@redhat.com', + author='Harald Hoyer, Arnaldo Carvalho de Melo, David Sommerseth', + author_email='davids@redhat.com', url='http://fedoraproject.org/wiki/python-ethtool', ext_modules=[ Extension( |