Skip to content

Commit

Permalink
Only open the NETLINK interface when needed
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
David Sommerseth committed Apr 11, 2011
1 parent abc7f91 commit 508ffff
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 75 deletions.
97 changes: 91 additions & 6 deletions python-ethtool/etherinfo.c
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
* Parts of this code is based on ideas and solutions in iproute2
Expand All @@ -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
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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);

Expand All @@ -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;
}
7 changes: 5 additions & 2 deletions python-ethtool/etherinfo.h
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
*
Expand All @@ -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
19 changes: 11 additions & 8 deletions python-ethtool/etherinfo_obj.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2010 Red Hat Inc.
* Copyright (C) 2009-2011 Red Hat Inc.
*
* David Sommerseth <[email protected]>
*
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 ) {
Expand Down Expand Up @@ -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 ) {
Expand Down
6 changes: 4 additions & 2 deletions python-ethtool/etherinfo_struct.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2010 Red Hat Inc.
* Copyright (C) 2009-2011 Red Hat Inc.
*
* David Sommerseth <[email protected]>
*
Expand Down Expand Up @@ -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 */
};

Expand Down
58 changes: 4 additions & 54 deletions python-ethtool/ethtool.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2008-2010 Red Hat Inc.
* Copyright (C) 2008-2011 Red Hat Inc.
*
* Arnaldo Carvalho de Melo <[email protected]>
* David Sommerseth <[email protected]>
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -1042,12 +998,6 @@ PyMODINIT_FUNC initethtool(void)
Py_INCREF(&ethtool_etherinfoIPv6Type);
PyModule_AddObject(m, "etherinfo_ipv6addr", (PyObject *)&ethtool_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. */
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import commands
import sys

version = '0.6'
version = '0.7'

ethtool = Extension('ethtool',
sources = ['python-ethtool/ethtool.c',
Expand Down Expand Up @@ -53,8 +53,8 @@ def _str2list(pkgstr, onlystr):
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(
Expand Down

0 comments on commit 508ffff

Please sign in to comment.