patch-2.4.0-test12 linux/net/ipx/af_ipx.c
Next file: linux/net/irda/ircomm/ircomm_core.c
Previous file: linux/net/ipv6/udp.c
Back to the patch index
Back to the overall index
- Lines: 1175
- Date:
Sun Dec 3 18:01:01 2000
- Orig file:
v2.4.0-test11/linux/net/ipx/af_ipx.c
- Orig date:
Tue Oct 31 12:42:27 2000
diff -u --recursive --new-file v2.4.0-test11/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
@@ -53,6 +53,14 @@
* Fixed connecting to primary net,
* Automatic binding on send & receive,
* Martijn van Oosterhout <kleptogimp@geocities.com>
+ * Revision 042: Multithreading - use spinlocks and refcounting to
+ * protect some structures: ipx_interface sock list, list
+ * of ipx interfaces, etc.
+ * Bugfixes - do refcounting on net_devices, check function
+ * results, etc. Thanks to davem and freitag for
+ * suggestions and guidance.
+ * Arnaldo Carvalho de Melo <acme@conectiva.com.br>,
+ * November, 2000
*
* Protect the module by a MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT
* pair. Also, now usage count is managed this way
@@ -104,24 +112,33 @@
#endif /* def MODULE */
/* Configuration Variables */
-static unsigned char ipxcfg_max_hops = 16;
-static char ipxcfg_auto_select_primary = 0;
-static char ipxcfg_auto_create_interfaces = 0;
+static unsigned char ipxcfg_max_hops = 16;
+static char ipxcfg_auto_select_primary;
+static char ipxcfg_auto_create_interfaces;
/* Global Variables */
-static struct datalink_proto *p8022_datalink = NULL;
-static struct datalink_proto *pEII_datalink = NULL;
-static struct datalink_proto *p8023_datalink = NULL;
-static struct datalink_proto *pSNAP_datalink = NULL;
+static struct datalink_proto *p8022_datalink;
+static struct datalink_proto *pEII_datalink;
+static struct datalink_proto *p8023_datalink;
+static struct datalink_proto *pSNAP_datalink;
static struct proto_ops ipx_dgram_ops;
static struct net_proto_family *spx_family_ops;
-static ipx_route *ipx_routes = NULL;
-static ipx_interface *ipx_interfaces = NULL;
-static ipx_interface *ipx_primary_net = NULL;
-static ipx_interface *ipx_internal_net = NULL;
+static ipx_route *ipx_routes;
+static rwlock_t ipx_routes_lock = RW_LOCK_UNLOCKED;
+
+static ipx_interface *ipx_interfaces;
+static spinlock_t ipx_interfaces_lock = SPIN_LOCK_UNLOCKED;
+
+static ipx_interface *ipx_primary_net;
+static ipx_interface *ipx_internal_net;
+
+#undef IPX_REFCNT_DEBUG
+#ifdef IPX_REFCNT_DEBUG
+atomic_t ipx_sock_nr;
+#endif
static int ipxcfg_set_auto_create(char val)
{
@@ -163,6 +180,26 @@
* *
\**************************************************************************/
+static inline void ipxitf_hold(ipx_interface *intrfc)
+{
+ atomic_inc(&intrfc->refcnt);
+}
+
+static void ipxitf_down(ipx_interface *intrfc);
+
+static inline void ipxitf_put(ipx_interface *intrfc)
+{
+ if (atomic_dec_and_test(&intrfc->refcnt))
+ ipxitf_down(intrfc);
+}
+
+static void __ipxitf_down(ipx_interface *intrfc);
+
+static inline void __ipxitf_put(ipx_interface *intrfc)
+{
+ if (atomic_dec_and_test(&intrfc->refcnt))
+ __ipxitf_down(intrfc);
+}
/*
* Note: Sockets may not be removed _during_ an interrupt or inet_bh
* handler using this technique. They can be added although we do not
@@ -173,25 +210,19 @@
{
struct sock *s;
ipx_interface *intrfc;
- unsigned long flags;
-
- save_flags(flags);
- cli();
/* Determine interface with which socket is associated */
intrfc = sk->protinfo.af_ipx.intrfc;
if(intrfc == NULL)
- {
- restore_flags(flags);
return;
- }
+ ipxitf_hold(intrfc);
+ spin_lock_bh(&intrfc->if_sklist_lock);
s = intrfc->if_sklist;
if(s == sk)
{
intrfc->if_sklist = s->next;
- restore_flags(flags);
- return;
+ goto out;
}
while(s && s->next)
@@ -199,29 +230,28 @@
if(s->next == sk)
{
s->next = sk->next;
- restore_flags(flags);
- return;
+ goto out;
}
s = s->next;
}
- restore_flags(flags);
+out: spin_unlock_bh(&intrfc->if_sklist_lock);
+ sock_put(sk);
+ ipxitf_put(intrfc);
}
-/*
- * This is only called from user mode. Thus it protects itself against
- * interrupt users but doesn't worry about being called during work.
- * Once it is removed from the queue no interrupt or bottom half will
- * touch it and we are (fairly 8-) ) safe.
- */
static void ipx_destroy_socket(struct sock *sk)
{
- struct sk_buff *skb;
-
ipx_remove_socket(sk);
- while((skb = skb_dequeue(&sk->receive_queue)) != NULL)
- kfree_skb(skb);
-
- sk_free(sk);
+ skb_queue_purge(&sk->receive_queue);
+#ifdef IPX_REFCNT_DEBUG
+ atomic_dec(&ipx_sock_nr);
+ printk(KERN_DEBUG "IPX socket %p released, %d are still alive\n", sk,
+ atomic_read(&ipx_sock_nr));
+ if (atomic_read(&sk->refcnt) != 1)
+ printk(KERN_DEBUG "Destruction sock ipx %p delayed, cnt=%d\n",
+ sk, atomic_read(&sk->refcnt));
+#endif
+ sock_put(sk);
}
/*
@@ -230,6 +260,8 @@
*/
static ipx_route * ipxrtr_lookup(__u32);
+/* ipxitf_clear_primary_net has to be called with ipx_interfaces_lock held */
+
static void ipxitf_clear_primary_net(void)
{
if(ipxcfg_auto_select_primary && (ipx_interfaces != NULL))
@@ -238,7 +270,8 @@
ipx_primary_net = NULL;
}
-static ipx_interface *ipxitf_find_using_phys(struct net_device *dev, unsigned short datalink)
+static ipx_interface *__ipxitf_find_using_phys(struct net_device *dev,
+ unsigned short datalink)
{
ipx_interface *i;
@@ -250,15 +283,33 @@
return (i);
}
+static ipx_interface *ipxitf_find_using_phys(struct net_device *dev,
+ unsigned short datalink)
+{
+ ipx_interface *i;
+
+ spin_lock_bh(&ipx_interfaces_lock);
+ i = __ipxitf_find_using_phys(dev, datalink);
+ if (i)
+ ipxitf_hold(i);
+ spin_unlock_bh(&ipx_interfaces_lock);
+ return i;
+}
+
static ipx_interface *ipxitf_find_using_net(__u32 net)
{
ipx_interface *i;
- if(!net)
- return (ipx_primary_net);
-
- for(i = ipx_interfaces; i && (i->if_netnum != net); i = i->if_next)
+ spin_lock_bh(&ipx_interfaces_lock);
+ if(net)
+ for(i = ipx_interfaces; i && (i->if_netnum != net);
+ i = i->if_next)
;
+ else
+ i = ipx_primary_net;
+ if (i)
+ ipxitf_hold(i);
+ spin_unlock_bh(&ipx_interfaces_lock);
return (i);
}
@@ -268,6 +319,9 @@
{
struct sock *s;
+ ipxitf_hold(intrfc);
+ sock_hold(sk);
+ spin_lock_bh(&intrfc->if_sklist_lock);
sk->protinfo.af_ipx.intrfc = intrfc;
sk->next = NULL;
if(intrfc->if_sklist == NULL)
@@ -278,9 +332,12 @@
;
s->next = sk;
}
+ spin_unlock_bh(&intrfc->if_sklist_lock);
+ ipxitf_put(intrfc);
}
-static struct sock *ipxitf_find_socket(ipx_interface *intrfc, unsigned short port)
+/* caller must hold intrfc->if_sklist_lock */
+static struct sock *__ipxitf_find_socket(ipx_interface *intrfc, unsigned short port)
{
struct sock *s;
@@ -288,6 +345,19 @@
(s != NULL) && (s->protinfo.af_ipx.port != port);
s = s->next)
;
+ return s;
+}
+
+/* caller must hold a reference to intrfc */
+static struct sock *ipxitf_find_socket(ipx_interface *intrfc, unsigned short port)
+{
+ struct sock *s;
+
+ spin_lock_bh(&intrfc->if_sklist_lock);
+ s = __ipxitf_find_socket(intrfc, port);
+ if (s)
+ sock_hold(s);
+ spin_unlock_bh(&intrfc->if_sklist_lock);
return (s);
}
@@ -297,7 +367,11 @@
static struct sock *ipxitf_find_internal_socket(ipx_interface *intrfc,
unsigned char *node, unsigned short port)
{
- struct sock *s = intrfc->if_sklist;
+ struct sock *s;
+
+ ipxitf_hold(intrfc);
+ spin_lock_bh(&intrfc->if_sklist_lock);
+ s = intrfc->if_sklist;
while(s != NULL)
{
@@ -308,6 +382,8 @@
}
s = s->next;
}
+ spin_unlock_bh(&intrfc->if_sklist_lock);
+ ipxitf_put(intrfc);
return (s);
}
@@ -315,7 +391,7 @@
static void ipxrtr_del_routes(ipx_interface *);
-static void ipxitf_down(ipx_interface *intrfc)
+static void __ipxitf_down(ipx_interface *intrfc)
{
ipx_interface *i;
struct sock *s, *t;
@@ -323,6 +399,7 @@
/* Delete all routes associated with this interface */
ipxrtr_del_routes(intrfc);
+ spin_lock_bh(&intrfc->if_sklist_lock);
/* error sockets */
for(s = intrfc->if_sklist; s != NULL; )
{
@@ -336,6 +413,7 @@
t->next = NULL;
}
intrfc->if_sklist = NULL;
+ spin_unlock_bh(&intrfc->if_sklist_lock);
/* remove this interface from list */
if(intrfc == ipx_interfaces)
@@ -356,12 +434,21 @@
if(intrfc == ipx_internal_net)
ipx_internal_net = NULL;
+ if (intrfc->if_dev)
+ dev_put(intrfc->if_dev);
kfree(intrfc);
MOD_DEC_USE_COUNT;
return;
}
+static void ipxitf_down(ipx_interface *intrfc)
+{
+ spin_lock_bh(&ipx_interfaces_lock);
+ __ipxitf_down(intrfc);
+ spin_unlock_bh(&ipx_interfaces_lock);
+}
+
static int ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *ptr)
{
struct net_device *dev = ptr;
@@ -370,14 +457,16 @@
if(event != NETDEV_DOWN)
return NOTIFY_DONE;
+ spin_lock_bh(&ipx_interfaces_lock);
for(i = ipx_interfaces; i != NULL;)
{
tmp = i->if_next;
if(i->if_dev == dev)
- ipxitf_down(i);
+ __ipxitf_put(i);
i = tmp;
}
+ spin_unlock_bh(&ipx_interfaces_lock);
return (NOTIFY_DONE);
}
@@ -396,15 +485,19 @@
* On input skb->sk is NULL. Nobody is charged for the memory.
*/
+/* caller must hold a reference to intrfc */
+
#ifdef CONFIG_IPX_INTERN
static int ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
{
struct ipxhdr *ipx = skb->nh.ipxh;
struct sock *s;
+ int ret;
int is_broadcast = (memcmp(ipx->ipx_dest.node, ipx_broadcast_node,
IPX_NODE_LEN) == 0);
+ spin_lock_bh(&intrfc->if_sklist_lock);
s = intrfc->if_sklist;
while(s != NULL)
@@ -420,8 +513,9 @@
if(copy != 0)
{
skb1 = skb_clone(skb, GFP_ATOMIC);
+ ret = -ENOMEM;
if (skb1 == NULL)
- return -ENOMEM;
+ goto out;
}
else
{
@@ -441,7 +535,9 @@
if(copy == 0)
kfree_skb(skb);
- return (0);
+ ret = 0;
+out: spin_unlock_bh(&intrfc->if_sklist_lock);
+ return ret;
}
#else
@@ -451,6 +547,7 @@
struct ipxhdr *ipx = skb->nh.ipxh;
struct sock *sock1 = NULL, *sock2 = NULL;
struct sk_buff *skb1 = NULL, *skb2 = NULL;
+ int ret;
if (intrfc == ipx_primary_net && ntohs(ipx->ipx_dest.sock) == 0x451)
{
@@ -490,10 +587,14 @@
* socket. Only these sockets have ipx_ncp_conn != 0, set
* by SIOCIPXNCPCONN.
*/
+ spin_lock_bh(&intrfc->if_sklist_lock);
for (sock1=intrfc->if_sklist;
(sock1 != NULL) &&
(sock1->protinfo.af_ipx.ipx_ncp_conn != connection);
sock1=sock1->next);
+ if (sock1)
+ sock_hold(sock1);
+ spin_unlock_bh(&intrfc->if_sklist_lock);
}
}
if (sock1 == NULL)
@@ -556,8 +657,9 @@
else
skb1 = skb;
+ ret = -ENOMEM;
if(skb1 == NULL)
- return (-ENOMEM);
+ goto out;
/* Do we need 2 SKBs? */
if(sock1 && sock2)
@@ -568,13 +670,19 @@
if(sock1)
(void) ipxitf_def_skb_handler(sock1, skb1);
+ ret = -ENOMEM;
if(skb2 == NULL)
- return (-ENOMEM);
+ goto out;
if(sock2)
(void) ipxitf_def_skb_handler(sock2, skb2);
- return (0);
+ ret = 0;
+out: if (sock1)
+ sock_put(sock1);
+ if (sock2)
+ sock_put(sock2);
+ return ret;
}
#endif /* CONFIG_IPX_INTERN */
@@ -603,6 +711,8 @@
return (skb2);
}
+/* caller must hold a reference to intrfc */
+
static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
{
struct ipxhdr *ipx = skb->nh.ipxh;
@@ -715,6 +825,9 @@
{
struct ipxhdr *ipx = skb->nh.ipxh;
ipx_interface *i;
+ int ret = 0;
+
+ ipxitf_hold(intrfc);
/* See if we should update our network number */
if(!intrfc->if_netnum /* net number of intrfc not known yet (== 0) */
@@ -738,6 +851,7 @@
ipx_frame_name(i->if_dlink_type),
ipx_device_name(intrfc),
ipx_frame_name(intrfc->if_dlink_type));
+ ipxitf_put(i);
}
}
@@ -770,6 +884,7 @@
*l = intrfc->if_netnum; /* insert recvd netnum into list */
ipx->ipx_tctrl++;
/* xmit on all other interfaces... */
+ spin_lock_bh(&ipx_interfaces_lock);
for(ifcs = ipx_interfaces; ifcs != NULL; ifcs = ifcs->if_next)
{
/* Except unconfigured interfaces */
@@ -785,9 +900,11 @@
{
ipx->ipx_dest.net = ifcs->if_netnum;
skb2=skb_clone(skb, GFP_ATOMIC);
- ipxrtr_route_skb(skb2);
+ if (skb2)
+ ipxrtr_route_skb(skb2);
}
}
+ spin_unlock_bh(&ipx_interfaces_lock);
/* Reset network number in packet */
ipx->ipx_dest.net = intrfc->if_netnum;
@@ -806,25 +923,27 @@
{
skb=skb_unshare(skb, GFP_ATOMIC);
if(skb)
- return (ipxrtr_route_skb(skb));
- else
- return (0);
+ ret = ipxrtr_route_skb(skb);
+ goto out_intrfc;
}
- kfree_skb(skb);
- return (0);
+ goto out_free_skb;
}
/* see if we should keep it */
if((memcmp(ipx_broadcast_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0)
|| (memcmp(intrfc->if_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0))
{
- return (ipxitf_demux_socket(intrfc, skb, 0));
+ ret = ipxitf_demux_socket(intrfc, skb, 0);
+ goto out_intrfc;
}
/* we couldn't pawn it off so unload it */
+out_free_skb:
kfree_skb(skb);
- return (0);
+out_intrfc:
+ ipxitf_put(intrfc);
+ return ret;
}
static void ipxitf_insert(ipx_interface *intrfc)
@@ -832,6 +951,7 @@
ipx_interface *i;
intrfc->if_next = NULL;
+ spin_lock_bh(&ipx_interfaces_lock);
if(ipx_interfaces == NULL)
ipx_interfaces = intrfc;
else
@@ -840,18 +960,18 @@
;
i->if_next = intrfc;
}
+ spin_unlock_bh(&ipx_interfaces_lock);
if(ipxcfg_auto_select_primary && (ipx_primary_net == NULL))
ipx_primary_net = intrfc;
- MOD_INC_USE_COUNT;
-
return;
}
static int ipxitf_create_internal(ipx_interface_definition *idef)
{
ipx_interface *intrfc;
+ int ret;
/* Only one primary network allowed */
if(ipx_primary_net != NULL)
@@ -860,8 +980,11 @@
/* Must have a valid network number */
if(!idef->ipx_network)
return (-EADDRNOTAVAIL);
- if(ipxitf_find_using_net(idef->ipx_network) != NULL)
+ intrfc = ipxitf_find_using_net(idef->ipx_network);
+ if(intrfc != NULL) {
+ ipxitf_put(intrfc);
return (-EADDRINUSE);
+ }
intrfc = (ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
if(intrfc == NULL)
@@ -877,9 +1000,15 @@
memcpy((char *)&(intrfc->if_node), idef->ipx_node, IPX_NODE_LEN);
ipx_internal_net = intrfc;
ipx_primary_net = intrfc;
+ spin_lock_init(&intrfc->if_sklist_lock);
+ atomic_set(&intrfc->refcnt, 1);
+ MOD_INC_USE_COUNT;
+ ipxitf_hold(intrfc);
ipxitf_insert(intrfc);
- return (ipxitf_add_local_route(intrfc));
+ ret = ipxitf_add_local_route(intrfc);
+ ipxitf_put(intrfc);
+ return ret;
}
static int ipx_map_frame_type(unsigned char type)
@@ -908,6 +1037,7 @@
unsigned short dlink_type = 0;
struct datalink_proto *datalink = NULL;
ipx_interface *intrfc;
+ int err;
if(idef->ipx_special == IPX_INTERNAL)
return (ipxitf_create_internal(idef));
@@ -915,11 +1045,16 @@
if((idef->ipx_special == IPX_PRIMARY) && (ipx_primary_net != NULL))
return (-EEXIST);
- if(idef->ipx_network
- && (ipxitf_find_using_net(idef->ipx_network) != NULL))
+ intrfc = ipxitf_find_using_net(idef->ipx_network);
+ if(idef->ipx_network && intrfc != NULL) {
+ ipxitf_put(intrfc);
return (-EADDRINUSE);
+ }
- dev = __dev_get_by_name(idef->ipx_device);
+ if (intrfc)
+ ipxitf_put(intrfc);
+
+ dev = dev_get_by_name(idef->ipx_device);
if(dev == NULL)
return (-ENODEV);
@@ -960,22 +1095,26 @@
break;
}
+ err = -ENETDOWN;
if(!(dev->flags & IFF_UP))
- return (-ENETDOWN);
+ goto out_dev;
/* Check addresses are suitable */
+ err = -EINVAL;
if(dev->addr_len > IPX_NODE_LEN)
- return (-EINVAL);
+ goto out_dev;
+ err = -EPROTONOSUPPORT;
if(datalink == NULL)
- return (-EPROTONOSUPPORT);
+ goto out_dev;
if((intrfc = ipxitf_find_using_phys(dev, dlink_type)) == NULL)
{
/* Ok now create */
intrfc = (ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
+ err = -EAGAIN;
if(intrfc == NULL)
- return (-EAGAIN);
+ goto out_dev;
intrfc->if_dev = dev;
intrfc->if_netnum = idef->ipx_network;
intrfc->if_dlink_type = dlink_type;
@@ -995,14 +1134,26 @@
}
else
memcpy(intrfc->if_node, idef->ipx_node, IPX_NODE_LEN);
+ spin_lock_init(&intrfc->if_sklist_lock);
+ atomic_set(&intrfc->refcnt, 1);
+ MOD_INC_USE_COUNT;
+ ipxitf_hold(intrfc);
ipxitf_insert(intrfc);
}
+
/* If the network number is known, add a route */
+ err = 0;
if(!intrfc->if_netnum)
- return (0);
+ goto out_intrfc;
- return (ipxitf_add_local_route(intrfc));
+ err = ipxitf_add_local_route(intrfc);
+out_intrfc:
+ ipxitf_put(intrfc);
+ return err;
+out_dev:
+ dev_put(dev);
+ return err;
}
static int ipxitf_delete(ipx_interface_definition *idef)
@@ -1010,33 +1161,40 @@
struct net_device *dev = NULL;
unsigned short dlink_type = 0;
ipx_interface *intrfc;
+ int ret = 0;
+ spin_lock_bh(&ipx_interfaces_lock);
if(idef->ipx_special == IPX_INTERNAL)
{
if(ipx_internal_net != NULL)
{
- ipxitf_down(ipx_internal_net);
- return (0);
+ __ipxitf_put(ipx_internal_net);
+ goto out;
}
- return (-ENOENT);
+ ret = -ENOENT;
+ goto out;
}
dlink_type = ipx_map_frame_type(idef->ipx_dlink_type);
- if(dlink_type == 0)
- return (-EPROTONOSUPPORT);
+ if(dlink_type == 0) {
+ ret = -EPROTONOSUPPORT;
+ goto out;
+ }
dev = __dev_get_by_name(idef->ipx_device);
- if(dev == NULL)
- return (-ENODEV);
+ if(dev == NULL) {
+ ret = -ENODEV;
+ goto out;
+ }
- intrfc = ipxitf_find_using_phys(dev, dlink_type);
+ intrfc = __ipxitf_find_using_phys(dev, dlink_type);
if(intrfc != NULL)
- {
- ipxitf_down(intrfc);
- return (0);
- }
+ __ipxitf_put(intrfc);
+ else
+ ret = -EINVAL;
- return (-EINVAL);
+out: spin_unlock_bh(&ipx_interfaces_lock);
+ return ret;
}
static ipx_interface *ipxitf_auto_create(struct net_device *dev,
@@ -1089,6 +1247,9 @@
memset(intrfc->if_node, 0, IPX_NODE_LEN);
memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]),
dev->dev_addr, dev->addr_len);
+ spin_lock_init(&intrfc->if_sklist_lock);
+ atomic_set(&intrfc->refcnt, 1);
+ MOD_INC_USE_COUNT;
ipxitf_insert(intrfc);
}
@@ -1151,6 +1312,7 @@
if(!copy_to_user(arg, &ifr, sizeof(ifr)))
err = 0;
+ ipxitf_put(ipxif);
return (err);
}
@@ -1187,12 +1349,16 @@
{
ipx_route *r;
+ read_lock_bh(&ipx_routes_lock);
for(r = ipx_routes; (r != NULL) && (r->ir_net != net); r = r->ir_next)
;
+ read_unlock_bh(&ipx_routes_lock);
return (r);
}
+/* caller must hold a reference to intrfc */
+
static int ipxrtr_add_route(__u32 network, ipx_interface *intrfc, unsigned char *node)
{
ipx_route *rt;
@@ -1204,8 +1370,11 @@
rt = (ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC);
if(rt == NULL)
return (-EAGAIN);
+
+ write_lock_bh(&ipx_routes_lock);
rt->ir_next = ipx_routes;
ipx_routes = rt;
+ write_unlock_bh(&ipx_routes_lock);
}
else if(intrfc == ipx_internal_net)
return (-EEXIST);
@@ -1230,6 +1399,7 @@
{
ipx_route **r, *tmp;
+ write_lock_bh(&ipx_routes_lock);
for(r = &ipx_routes; (tmp = *r) != NULL;)
{
if(tmp->ir_intrfc == intrfc)
@@ -1240,42 +1410,50 @@
else
r = &(tmp->ir_next);
}
+ write_unlock_bh(&ipx_routes_lock);
}
static int ipxrtr_create(ipx_route_definition *rd)
{
ipx_interface *intrfc;
+ int ret;
/* Find the appropriate interface */
intrfc = ipxitf_find_using_net(rd->ipx_router_network);
if(intrfc == NULL)
return (-ENETUNREACH);
-
- return (ipxrtr_add_route(rd->ipx_network, intrfc, rd->ipx_router_node));
+ ret = ipxrtr_add_route(rd->ipx_network, intrfc, rd->ipx_router_node);
+ ipxitf_put(intrfc);
+ return ret;
}
static int ipxrtr_delete(long net)
{
ipx_route **r;
ipx_route *tmp;
+ int err;
+ write_lock_bh(&ipx_routes_lock);
for(r = &ipx_routes; (tmp = *r) != NULL;)
{
if(tmp->ir_net == net)
{
/* Directly connected; can't lose route */
+ err = -EPERM;
if(!(tmp->ir_routed))
- return (-EPERM);
+ goto out;
*r = tmp->ir_next;
kfree(tmp);
- return (0);
+ err = 0;
+ goto out;
}
r = &(tmp->ir_next);
}
-
- return (-ENOENT);
+ err = -ENOENT;
+out: write_unlock_bh(&ipx_routes_lock);
+ return err;
}
/*
@@ -1352,13 +1530,14 @@
intrfc = rt->ir_intrfc;
}
+ ipxitf_hold(intrfc);
ipx_offset = intrfc->if_ipx_offset;
size = sizeof(struct ipxhdr) + len;
size += ipx_offset;
skb = sock_alloc_send_skb(sk, size, 0, noblock, &err);
if(skb == NULL)
- return (err);
+ goto out;
skb_reserve(skb,ipx_offset);
skb->sk = sk;
@@ -1397,7 +1576,7 @@
if(err)
{
kfree_skb(skb);
- return (-EFAULT);
+ goto out;
}
/* Apply checksum. Not allowed on 802.3 links. */
@@ -1406,14 +1585,15 @@
else
ipx->ipx_checksum = ipx_set_checksum(ipx, len + sizeof(struct ipxhdr));
- return (ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ?
- rt->ir_router_node : ipx->ipx_dest.node));
+ err = ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ?
+ rt->ir_router_node : ipx->ipx_dest.node);
+out: ipxitf_put(intrfc);
+ return err;
}
int ipxrtr_route_skb(struct sk_buff *skb)
{
struct ipxhdr *ipx = skb->nh.ipxh;
- ipx_interface *i;
ipx_route *r;
r = ipxrtr_lookup(ipx->ipx_dest.net);
@@ -1423,9 +1603,10 @@
return (0);
}
- i = r->ir_intrfc;
- (void)ipxitf_send(i, skb, (r->ir_routed) ?
+ ipxitf_hold(r->ir_intrfc);
+ (void)ipxitf_send(r->ir_intrfc, skb, (r->ir_routed) ?
r->ir_router_node : ipx->ipx_dest.node);
+ ipxitf_put(r->ir_intrfc);
return (0);
}
@@ -1512,8 +1693,13 @@
/* Theory.. Keep printing in the same place until we pass offset */
- len += sprintf(buffer,"%-11s%-15s%-9s%-11s%s\n", "Network",
+ len += sprintf(buffer,"%-11s%-15s%-9s%-11s%s", "Network",
"Node_Address", "Primary", "Device", "Frame_Type");
+#ifdef IPX_REFCNT_DEBUG
+ len += sprintf(buffer + len, " refcnt");
+#endif
+ strcat(buffer+len++, "\n");
+ spin_lock_bh(&ipx_interfaces_lock);
for(i = ipx_interfaces; i != NULL; i = i->if_next)
{
len += sprintf(buffer+len, "%08lX ", (long unsigned int)ntohl(i->if_netnum));
@@ -1523,9 +1709,12 @@
len += sprintf(buffer+len, "%-9s", (i == ipx_primary_net) ?
"Yes" : "No");
len += sprintf(buffer+len, "%-11s", ipx_device_name(i));
- len += sprintf(buffer+len, "%s\n",
+ len += sprintf(buffer+len, "%-9s",
ipx_frame_name(i->if_dlink_type));
-
+#ifdef IPX_REFCNT_DEBUG
+ len += sprintf(buffer+len,"%6d",atomic_read(&i->refcnt));
+#endif
+ strcat(buffer+len++, "\n");
/* Are we still dumping unwanted data then discard the record */
pos = begin + len;
@@ -1537,6 +1726,7 @@
if(pos > offset + length) /* We have dumped enough */
break;
}
+ spin_unlock_bh(&ipx_interfaces_lock);
/* The data in question runs from begin to begin+len */
*start = buffer + (offset - begin); /* Start of wanted data */
@@ -1564,8 +1754,11 @@
"Remote_Address", "Tx_Queue", "Rx_Queue",
"State", "Uid");
+ spin_lock_bh(&ipx_interfaces_lock);
for(i = ipx_interfaces; i != NULL; i = i->if_next)
{
+ ipxitf_hold(i);
+ spin_lock_bh(&i->if_sklist_lock);
for(s = i->if_sklist; s != NULL; s = s->next)
{
#ifdef CONFIG_IPX_INTERN
@@ -1617,7 +1810,10 @@
if(pos > offset + length) /* We have dumped enough */
break;
}
+ spin_unlock_bh(&i->if_sklist_lock);
+ ipxitf_put(i);
}
+ spin_unlock_bh(&ipx_interfaces_lock);
/* The data in question runs from begin to begin+len */
*start = buffer + (offset-begin);
@@ -1636,6 +1832,7 @@
len += sprintf(buffer,"%-11s%-13s%s\n",
"Network", "Router_Net", "Router_Node");
+ read_lock_bh(&ipx_routes_lock);
for(rt = ipx_routes; rt != NULL; rt = rt->ir_next)
{
len += sprintf(buffer+len,"%08lX ", (long unsigned int) ntohl(rt->ir_net));
@@ -1663,6 +1860,7 @@
if(pos > offset + length)
break;
}
+ read_unlock_bh(&ipx_routes_lock);
*start = buffer + (offset - begin);
len -= (offset - begin);
@@ -1777,7 +1975,11 @@
default:
return (-ESOCKTNOSUPPORT);
}
-
+#ifdef IPX_REFCNT_DEBUG
+ atomic_inc(&ipx_sock_nr);
+ printk(KERN_DEBUG "IPX socket %p created, now we have %d alive\n", sk,
+ atomic_read(&ipx_sock_nr));
+#endif
sock_init_data(sock, sk);
sk->destruct = NULL;
sk->no_check = 1; /* Checksum off by default */
@@ -1807,14 +2009,18 @@
return (0);
}
+/* caller must hold a referente to intrfc */
+
static unsigned short ipx_first_free_socketnum(ipx_interface *intrfc)
{
unsigned short socketNum = intrfc->if_sknum;
+ spin_lock_bh(&intrfc->if_sklist_lock);
+
if(socketNum < IPX_MIN_EPHEMERAL_SOCKET)
socketNum = IPX_MIN_EPHEMERAL_SOCKET;
- while(ipxitf_find_socket(intrfc, ntohs(socketNum)) != NULL)
+ while(__ipxitf_find_socket(intrfc, ntohs(socketNum)) != NULL)
{
if(socketNum > IPX_MAX_EPHEMERAL_SOCKET)
socketNum = IPX_MIN_EPHEMERAL_SOCKET;
@@ -1822,6 +2028,7 @@
socketNum++;
}
+ spin_unlock_bh(&intrfc->if_sklist_lock);
intrfc->if_sknum = socketNum;
return (ntohs(socketNum));
@@ -1832,6 +2039,7 @@
struct sock *sk;
ipx_interface *intrfc;
struct sockaddr_ipx *addr = (struct sockaddr_ipx *)uaddr;
+ int ret;
sk = sock->sk;
@@ -1848,13 +2056,15 @@
if(addr->sipx_port == 0)
{
addr->sipx_port = ipx_first_free_socketnum(intrfc);
+ ret = -EINVAL;
if(addr->sipx_port == 0)
- return (-EINVAL);
+ goto out;
}
/* protect IPX system stuff like routing/sap */
+ ret = -EACCES;
if(ntohs(addr->sipx_port) < IPX_MIN_EPHEMERAL_SOCKET && !capable(CAP_NET_ADMIN))
- return (-EACCES);
+ goto out;
sk->protinfo.af_ipx.port = addr->sipx_port;
@@ -1866,8 +2076,9 @@
* node number 0 was specified, the default is used.
*/
+ ret = -EINVAL;
if(memcmp(addr->sipx_node,ipx_broadcast_node,IPX_NODE_LEN) == 0)
- return (-EINVAL);
+ goto out;
if(memcmp(addr->sipx_node, ipx_this_node, IPX_NODE_LEN) == 0)
{
memcpy(sk->protinfo.af_ipx.node, intrfc->if_node,
@@ -1878,6 +2089,7 @@
memcpy(sk->protinfo.af_ipx.node, addr->sipx_node, IPX_NODE_LEN);
}
+ ret = -EADDRINUSE;
if(ipxitf_find_internal_socket(intrfc,
sk->protinfo.af_ipx.node,
sk->protinfo.af_ipx.port) != NULL)
@@ -1885,7 +2097,7 @@
SOCK_DEBUG(sk,
"IPX: bind failed because port %X in use.\n",
ntohs((int)addr->sipx_port));
- return (-EADDRINUSE);
+ goto out;
}
}
else
@@ -1898,12 +2110,13 @@
memcpy(sk->protinfo.af_ipx.node, intrfc->if_node,
IPX_NODE_LEN);
+ ret = -EADDRINUSE;
if(ipxitf_find_socket(intrfc, addr->sipx_port) != NULL)
{
SOCK_DEBUG(sk,
"IPX: bind failed because port %X in use.\n",
ntohs((int)addr->sipx_port));
- return (-EADDRINUSE);
+ goto out;
}
}
@@ -1912,11 +2125,12 @@
/* Source addresses are easy. It must be our network:node pair for
an interface routed to IPX with the ipx routing ioctl() */
+ ret = -EADDRINUSE;
if(ipxitf_find_socket(intrfc, addr->sipx_port) != NULL)
{
SOCK_DEBUG(sk, "IPX: bind failed because port %X in use.\n",
ntohs((int)addr->sipx_port));
- return (-EADDRINUSE);
+ goto out;
}
#endif /* CONFIG_IPX_INTERN */
@@ -1925,7 +2139,9 @@
sk->zapped = 0;
SOCK_DEBUG(sk, "IPX: bound socket 0x%04X.\n", ntohs(addr->sipx_port) );
- return (0);
+ ret = 0;
+out: ipxitf_put(intrfc);
+ return ret;
}
static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
@@ -2037,6 +2253,7 @@
/* NULL here for pt means the packet was looped back */
ipx_interface *intrfc;
struct ipxhdr *ipx;
+ int ret;
ipx = skb->nh.ipxh;
@@ -2066,14 +2283,16 @@
&& ntohl(ipx->ipx_dest.net) != 0L)
{
intrfc = ipxitf_auto_create(dev, pt->type);
+ ipxitf_hold(intrfc);
}
if(intrfc == NULL) /* Not one of ours */
goto drop;
}
- return (ipxitf_rcv(intrfc, skb));
-
+ ret = ipxitf_rcv(intrfc, skb);
+ ipxitf_put(intrfc);
+ return ret;
drop:
kfree_skb(skb);
return (0);
@@ -2412,8 +2631,9 @@
proc_net_create("ipx_route", 0, ipx_rt_get_info);
#endif
- printk(KERN_INFO "NET4: Linux IPX 0.38 for NET4.0\n");
+ printk(KERN_INFO "NET4: Linux IPX 0.42v4 for NET4.0\n");
printk(KERN_INFO "IPX Portions Copyright (c) 1995 Caldera, Inc.\n");
+ printk(KERN_INFO "IPX Portions Copyright (c) 2000 Conectiva, Inc.\n");
return 0;
}
module_init(ipx_init);
@@ -2451,21 +2671,14 @@
#ifdef MODULE
static void ipx_proto_finito(void)
{
- ipx_interface *ifc;
+ /* no need to worry about having anything on the ipx_interfaces
+ * list, when a interface is created we increment the module
+ * usage count, so the module will only be unloaded when there
+ * are no more interfaces */
- while(ipx_interfaces)
- {
- ifc = ipx_interfaces;
- ipx_interfaces = ifc->if_next;
- ifc->if_next = NULL;
- ipxitf_down(ifc);
- }
-
-#ifdef CONFIG_PROC_FS
proc_net_remove("ipx_route");
proc_net_remove("ipx_interface");
proc_net_remove("ipx");
-#endif
unregister_netdevice_notifier(&ipx_dev_notifier);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)