Skip to content

Commit 508ffff

Browse files
author
David Sommerseth
committed
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>
1 parent abc7f91 commit 508ffff

File tree

6 files changed

+118
-75
lines changed

6 files changed

+118
-75
lines changed

python-ethtool/etherinfo.c

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* etherinfo.c - Retrieve ethernet interface info via NETLINK
22
*
3-
* Copyright (C) 2009-2010 Red Hat Inc.
3+
* Copyright (C) 2009-2011 Red Hat Inc.
44
*
55
* David Sommerseth <davids@redhat.com>
66
* Parts of this code is based on ideas and solutions in iproute2
@@ -27,9 +27,12 @@
2727
#include <netlink/route/rtnl.h>
2828
#include <assert.h>
2929
#include <errno.h>
30+
#include <pthread.h>
3031
#include "etherinfo_struct.h"
3132
#include "etherinfo.h"
3233

34+
pthread_mutex_t nlc_counter_mtx = PTHREAD_MUTEX_INITIALIZER;
35+
3336
/*
3437
*
3538
* Internal functions for working with struct etherinfo
@@ -274,15 +277,25 @@ void dump_etherinfo(FILE *fp, struct etherinfo *ptr)
274277
*
275278
* @return Returns 1 on success, otherwise 0.
276279
*/
277-
int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query)
280+
int get_etherinfo(struct etherinfo_obj_data *data, nlQuery query)
278281
{
279282
struct nl_cache *link_cache;
280283
struct nl_cache *addr_cache;
281284
struct rtnl_addr *addr;
282285
struct rtnl_link *link;
286+
struct etherinfo *ethinf = NULL;
283287
int ret = 0;
284288

285-
if( !ethinf || !nlc ) {
289+
if( !data || !data->ethinfo ) {
290+
return 0;
291+
}
292+
ethinf = data->ethinfo;
293+
294+
/* Open a NETLINK connection on-the-fly */
295+
if( !open_netlink(data) ) {
296+
PyErr_Format(PyExc_RuntimeError,
297+
"Could not open a NETLINK connection for %s",
298+
ethinf->device);
286299
return 0;
287300
}
288301

@@ -291,7 +304,7 @@ int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query
291304
* interface index if we have that
292305
*/
293306
if( ethinf->index < 0 ) {
294-
link_cache = rtnl_link_alloc_cache(nlc);
307+
link_cache = rtnl_link_alloc_cache(*data->nlc);
295308
ethinf->index = rtnl_link_name2i(link_cache, ethinf->device);
296309
if( ethinf->index < 0 ) {
297310
return 0;
@@ -303,7 +316,7 @@ int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query
303316
switch( query ) {
304317
case NLQRY_LINK:
305318
/* Extract MAC/hardware address of the interface */
306-
link_cache = rtnl_link_alloc_cache(nlc);
319+
link_cache = rtnl_link_alloc_cache(*data->nlc);
307320
link = rtnl_link_alloc();
308321
rtnl_link_set_ifindex(link, ethinf->index);
309322
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
314327

315328
case NLQRY_ADDR:
316329
/* Extract IP address information */
317-
addr_cache = rtnl_addr_alloc_cache(nlc);
330+
addr_cache = rtnl_addr_alloc_cache(*data->nlc);
318331
addr = rtnl_addr_alloc();
319332
rtnl_addr_set_ifindex(addr, ethinf->index);
320333

@@ -337,3 +350,75 @@ int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query
337350
return ret;
338351
}
339352

353+
354+
/**
355+
* Connects to the NETLINK interface. This will be called
356+
* for each etherinfo object being generated, and it will
357+
* keep a separate file descriptor open for each object
358+
*
359+
* @param data etherinfo_obj_data structure
360+
*
361+
* @return Returns 1 on success, otherwise 0.
362+
*/
363+
int open_netlink(struct etherinfo_obj_data *data)
364+
{
365+
if( !data ) {
366+
return 0;
367+
}
368+
369+
/* Reuse already established NETLINK connection, if a connection exists */
370+
if( *data->nlc ) {
371+
/* If this object has not used NETLINK earlier, tag it as a user */
372+
if( !data->nlc_active ) {
373+
pthread_mutex_lock(&nlc_counter_mtx);
374+
(*data->nlc_users)++;
375+
pthread_mutex_unlock(&nlc_counter_mtx);
376+
}
377+
data->nlc_active = 1;
378+
return 1;
379+
}
380+
381+
/* No earlier connections exists, establish a new one */
382+
*data->nlc = nl_handle_alloc();
383+
nl_connect(*data->nlc, NETLINK_ROUTE);
384+
if( (*data->nlc != NULL) ) {
385+
/* Tag this object as an active user */
386+
pthread_mutex_lock(&nlc_counter_mtx);
387+
(*data->nlc_users)++;
388+
pthread_mutex_unlock(&nlc_counter_mtx);
389+
data->nlc_active = 1;
390+
return 1;
391+
} else {
392+
return 0;
393+
}
394+
}
395+
396+
397+
/**
398+
* Closes the NETLINK connection. This should be called automatically whenever
399+
* the corresponding etherinfo object is deleted.
400+
*
401+
* @param ptr Pointer to the pointer of struct nl_handle, which contains the NETLINK connection
402+
*/
403+
void close_netlink(struct etherinfo_obj_data *data)
404+
{
405+
if( !data || !(*data->nlc) ) {
406+
return;
407+
}
408+
409+
/* Untag this object as a NETLINK user */
410+
data->nlc_active = 0;
411+
pthread_mutex_lock(&nlc_counter_mtx);
412+
(*data->nlc_users)--;
413+
pthread_mutex_unlock(&nlc_counter_mtx);
414+
415+
/* Don't close the connection if there are more users */
416+
if( *data->nlc_users > 0) {
417+
return;
418+
}
419+
420+
/* Close NETLINK connection */
421+
nl_close(*data->nlc);
422+
nl_handle_destroy(*data->nlc);
423+
*data->nlc = NULL;
424+
}

python-ethtool/etherinfo.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* etherinfo.h - Retrieve ethernet interface info via NETLINK
22
*
3-
* Copyright (C) 2009-2010 Red Hat Inc.
3+
* Copyright (C) 2009-2011 Red Hat Inc.
44
*
55
* David Sommerseth <davids@redhat.com>
66
*
@@ -26,10 +26,13 @@
2626

2727
typedef enum {NLQRY_LINK, NLQRY_ADDR} nlQuery; /**< Supported query types in the etherinfo code */
2828

29-
int get_etherinfo(struct etherinfo *ethinf, struct nl_handle *nlc, nlQuery query);
29+
int get_etherinfo(struct etherinfo_obj_data *data, nlQuery query);
3030
void free_etherinfo(struct etherinfo *ptr);
3131
void dump_etherinfo(FILE *, struct etherinfo *);
3232

3333
void free_ipv6addresses(struct ipv6address *ptr);
3434

35+
int open_netlink(struct etherinfo_obj_data *);
36+
void close_netlink(struct etherinfo_obj_data *);
37+
3538
#endif

python-ethtool/etherinfo_obj.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2009-2010 Red Hat Inc.
2+
* Copyright (C) 2009-2011 Red Hat Inc.
33
*
44
* David Sommerseth <davids@redhat.com>
55
*
@@ -40,6 +40,8 @@ extern PyTypeObject ethtool_etherinfoIPv6Type;
4040
void _ethtool_etherinfo_dealloc(etherinfo_py *self)
4141
{
4242
if( self->data ) {
43+
close_netlink(self->data);
44+
4345
if( self->data->ethinfo ) {
4446
free_etherinfo(self->data->ethinfo);
4547
}
@@ -110,20 +112,21 @@ PyObject *_ethtool_etherinfo_getter(etherinfo_py *self, PyObject *attr_o)
110112
if( strcmp(attr, "device") == 0 ) {
111113
ret = RETURN_STRING(self->data->ethinfo->device);
112114
} else if( strcmp(attr, "mac_address") == 0 ) {
113-
get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_LINK);
115+
get_etherinfo(self->data, NLQRY_LINK);
114116
ret = RETURN_STRING(self->data->ethinfo->hwaddress);
115117
} else if( strcmp(attr, "ipv4_address") == 0 ) {
116-
get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_ADDR);
118+
get_etherinfo(self->data, NLQRY_ADDR);
117119
ret = RETURN_STRING(self->data->ethinfo->ipv4_address);
118120
} else if( strcmp(attr, "ipv4_netmask") == 0 ) {
119-
get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_ADDR);
121+
get_etherinfo(self->data, NLQRY_ADDR);
120122
ret = PyInt_FromLong(self->data->ethinfo->ipv4_netmask);
121123
} else if( strcmp(attr, "ipv4_broadcast") == 0 ) {
122-
get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_ADDR);
124+
get_etherinfo(self->data, NLQRY_ADDR);
123125
ret = RETURN_STRING(self->data->ethinfo->ipv4_broadcast);
124126
} else {
125127
ret = PyObject_GenericGetAttr((PyObject *)self, attr_o);
126128
}
129+
127130
return ret;
128131
}
129132

@@ -160,8 +163,8 @@ PyObject *_ethtool_etherinfo_str(etherinfo_py *self)
160163
return NULL;
161164
}
162165

163-
get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_LINK);
164-
get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_ADDR);
166+
get_etherinfo(self->data, NLQRY_LINK);
167+
get_etherinfo(self->data, NLQRY_ADDR);
165168

166169
ret = PyString_FromFormat("Device %s:\n", self->data->ethinfo->device);
167170
if( self->data->ethinfo->hwaddress ) {
@@ -223,7 +226,7 @@ PyObject * _ethtool_etherinfo_get_ipv6_addresses(etherinfo_py *self, PyObject *n
223226
return NULL;
224227
}
225228

226-
get_etherinfo(self->data->ethinfo, self->data->nlc, NLQRY_ADDR);
229+
get_etherinfo(self->data, NLQRY_ADDR);
227230
ipv6 = self->data->ethinfo->ipv6_addresses;
228231
ret = PyTuple_New(1);
229232
if( !ret ) {

python-ethtool/etherinfo_struct.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2009-2010 Red Hat Inc.
2+
* Copyright (C) 2009-2011 Red Hat Inc.
33
*
44
* David Sommerseth <davids@redhat.com>
55
*
@@ -58,7 +58,9 @@ struct ipv6address {
5858
*
5959
*/
6060
struct etherinfo_obj_data {
61-
struct nl_handle *nlc; /**< Contains NETLINK connection info */
61+
struct nl_handle **nlc; /**< Contains NETLINK connection info (global) */
62+
unsigned int *nlc_users; /**< Resource counter for the NETLINK connection (global) */
63+
unsigned short nlc_active; /**< Is this instance using NETLINK? */
6264
struct etherinfo *ethinfo; /**< Contains info about our current interface */
6365
};
6466

python-ethtool/ethtool.c

Lines changed: 4 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2008-2010 Red Hat Inc.
2+
* Copyright (C) 2008-2011 Red Hat Inc.
33
*
44
* Arnaldo Carvalho de Melo <acme@redhat.com>
55
* David Sommerseth <davids@redhat.com>
@@ -32,6 +32,7 @@
3232
#include "etherinfo.h"
3333

3434
static struct nl_handle *nlconnection = NULL;
35+
unsigned int nlconnection_users = 0; /* How many NETLINK users are active? */
3536
extern PyTypeObject ethtool_etherinfoType;
3637
extern PyTypeObject ethtool_etherinfoIPv6Type;
3738

@@ -314,7 +315,8 @@ static PyObject *get_interfaces_info(PyObject *self __unused, PyObject *args) {
314315
*/
315316
objdata->ethinfo->device = strdup(fetch_devs[i]);
316317
objdata->ethinfo->index = -1;
317-
objdata->nlc = nlconnection; /* Global variable */
318+
objdata->nlc = &nlconnection;
319+
objdata->nlc_users = &nlconnection_users;
318320

319321
/* Instantiate a new etherinfo object with the device information */
320322
ethinf_py = PyCObject_FromVoidPtr(objdata, NULL);
@@ -979,52 +981,6 @@ static struct PyMethodDef PyEthModuleMethods[] = {
979981
};
980982

981983

982-
/**
983-
* Connects to the NETLINK interface. This should only be
984-
* called once as part of the main ethtool module init.
985-
*
986-
* @param nlc Structure which keeps the NETLINK connection handle (struct nl_handle)
987-
*
988-
* @return Returns 1 on success, otherwise 0.
989-
*/
990-
int open_netlink(struct nl_handle **nlc)
991-
{
992-
if( *nlc ) {
993-
return 0;
994-
}
995-
996-
*nlc = nl_handle_alloc();
997-
nl_connect(*nlc, NETLINK_ROUTE);
998-
return (*nlc != NULL);
999-
}
1000-
1001-
1002-
/**
1003-
* Closes the NETLINK connection. This should be called automatically whenever
1004-
* the ethtool module is unloaded from Python.
1005-
*
1006-
* @param ptr Pointer to the pointer of struct nl_handle, which contains the NETLINK connection
1007-
*/
1008-
void close_netlink(void **ptr)
1009-
{
1010-
struct nl_handle *nlc;
1011-
1012-
if( !ptr && !*ptr ) {
1013-
return;
1014-
}
1015-
1016-
nlc = (struct nl_handle *) *ptr;
1017-
if( !nlc ) {
1018-
return;
1019-
}
1020-
1021-
/* Close NETLINK connection */
1022-
nl_close(nlc);
1023-
nl_handle_destroy(nlc);
1024-
*ptr = NULL; /* reset the pointers pointer address */
1025-
}
1026-
1027-
1028984
PyMODINIT_FUNC initethtool(void)
1029985
{
1030986
PyObject *m;
@@ -1042,12 +998,6 @@ PyMODINIT_FUNC initethtool(void)
1042998
Py_INCREF(&ethtool_etherinfoIPv6Type);
1043999
PyModule_AddObject(m, "etherinfo_ipv6addr", (PyObject *)&ethtool_etherinfoIPv6Type);
10441000

1045-
// Prepare an internal netlink connection object
1046-
if( open_netlink(&nlconnection) ) {
1047-
PyModule_AddObject(m, "__nlconnection",
1048-
PyCObject_FromVoidPtr(&nlconnection, close_netlink));
1049-
}
1050-
10511001
// Setup constants
10521002
PyModule_AddIntConstant(m, "IFF_UP", IFF_UP); /* Interface is up. */
10531003
PyModule_AddIntConstant(m, "IFF_BROADCAST", IFF_BROADCAST); /* Broadcast address valid. */

setup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import commands
55
import sys
66

7-
version = '0.6'
7+
version = '0.7'
88

99
ethtool = Extension('ethtool',
1010
sources = ['python-ethtool/ethtool.c',
@@ -53,8 +53,8 @@ def _str2list(pkgstr, onlystr):
5353
setup(name='ethtool',
5454
version=version,
5555
description='Python module to interface with ethtool',
56-
author='Harald Hoyer & Arnaldo Carvalho de Melo',
57-
author_email='acme@redhat.com',
56+
author='Harald Hoyer, Arnaldo Carvalho de Melo, David Sommerseth',
57+
author_email='davids@redhat.com',
5858
url='http://fedoraproject.org/wiki/python-ethtool',
5959
ext_modules=[
6060
Extension(

0 commit comments

Comments
 (0)