[patch 3/8] openmosix/fix_pkread_potential_deadlock.patch Fix lock and schedule usage in pktread
Florian Delizy <[email protected]> Thu, 23 Nov 2006 19:38:56 +0100
| Newsgroups | gmane.linux.cluster.openmosix.devel |
|---|---|
| Message-ID | <[email protected]> |
This patch fix the lock usage of pktread, since pktread is called under lock (write_lock) by kcomd, it should not schedule (nor write_lock again on the same ressource) Thanks to Carl Radford who helped me unveil this problem. ------------------------------------------------------------------------- Take Surveys. Earn Cash. Influence the Future of IT Join SourceForge.net's Techsay panel and you'll get the chance to share your opinions on IT & business topics through brief surveys - and earn cash http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV _______________________________________________ openMosix-devel mailing list openMosix-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org https://lists.sourceforge.net/lists/listinfo/openmosix-devel
fix_pkread_potential_deadlock.patch
(text/x-patch, 16.6 KB)
Subject: [patch @num@/@total@] @name@ Fix lock and schedule usage in pktread
This patch fix the lock usage of pktread, since pktread is called under lock (write_lock)
by kcomd, it should not schedule (nor write_lock again on the same ressource)
Thanks to Carl Radford who helped me unveil this problem.
Index: linux/include/hpc/kcom.h
===================================================================
--- linux.orig/include/hpc/kcom.h 2006-11-20 20:28:33.000000000 +0100
+++ linux/include/hpc/kcom.h 2006-11-23 18:42:53.000000000 +0100
@@ -114,12 +114,13 @@
struct kcom_node
{
- int fd; /* fd to send packet */
- struct socket *sock; /* socket */
- struct sockaddr addr; /* addr of this node */
- rwlock_t tasks_lock; /* lock for the list */
- struct list_head tasks; /* list of task */
- struct list_head list; /* list of nodes */
+ int fd; /* fd to send packet */
+ struct socket *sock; /* socket */
+ struct sockaddr addr; /* addr of this node */
+ rwlock_t tasks_lock; /* lock for the list */
+ struct list_head tasks; /* list of task */
+ struct list_head list; /* list of nodes */
+ struct list_head process_list; /* list used internally by kcomd */
};
@@ -134,9 +135,13 @@
// struct kcom_pkt in_packs;
struct list_head in_packs;
- rwlock_t out_packs_lock; // List structure locks ...
+ rwlock_t out_packs_lock; /* List structure locks ...*/
rwlock_t in_packs_lock;
};
extern int kcom_send_nack(task_t *p, struct kcom_pkt *recv_pkt);
+extern int kcom_node_del(struct sockaddr *addr);
+extern void kcom_node_sock_release(struct kcom_node *node);
+extern struct kcom_node *__create_connection(struct sockaddr *saddr
+ ,struct kcom_node *node);
#endif /* _HPC_KCOM_H */
Index: linux/hpc/kcom.c
===================================================================
--- linux.orig/hpc/kcom.c 2006-11-20 20:46:29.000000000 +0100
+++ linux/hpc/kcom.c 2006-11-23 18:42:53.000000000 +0100
@@ -290,8 +290,11 @@
struct msghdr msg = { NULL, 0, &iov, 1, NULL, 0, MSG_WAITALL | MSG_NOSIGNAL };
mm_segment_t oldfs;
int i;
- int nb_retries = 0;
char buf[32];
+ int nb_retries = 0;
+
+ if (!sock)
+ return -ENOENT;
iov.iov_base = data;
iov.iov_len = len;
@@ -312,9 +315,9 @@
if ((i == -ENOSPC) || (i == -EAGAIN)) {
/* Prevent infinite loop 60s */
- if (60000 > nb_retries) {
- OMBUG("too many retries\n");
- len = -1;
+ if (60000 < nb_retries++) {
+ printk(KERN_ERR "too many retries\n");
+ len = -ETIMEDOUT;
goto read_exit;
}
@@ -323,8 +326,8 @@
}
if (i < 0) {
- OMBUG("error %d receiving data.\n", i);
- len = -1;
+ printk(KERN_ERR "error %d receiving data.\n", i);
+ len = i;
goto read_exit;
}
iov.iov_base += i;
@@ -353,19 +356,33 @@
* read the pkt header of the data transmission.
* The hdr indicates the type and size of the data.
* All packet headers are the same size.
+ * The packet is allocated; caller must free it
**/
-struct kcom_pkt* pkt_hdr_read(struct kcom_node *node)
+int pkt_hdr_read(struct kcom_node *node, struct kcom_pkt **recv_kcom_pkt)
{
struct iovec iov;
struct msghdr msg = { NULL, 0, &iov, 1, NULL, 0, MSG_WAITALL | MSG_NOSIGNAL };
- struct kcom_pkt *recv_kcom_pkt;
+ struct kcom_pkt *recv_pkt;
mm_segment_t oldfs;
struct socket *sock=node->sock;
int i;
+ int err = 0;
int retry = 0;
+ int first_loop = 1;
+
+ *recv_kcom_pkt = NULL;
+
+ if (!sock)
+ return -ENODEV;
- recv_kcom_pkt = kmem_cache_alloc(kcom_pkt_cachep, SLAB_KERNEL);
- iov.iov_base = recv_kcom_pkt;
+ recv_pkt = kmem_cache_alloc(kcom_pkt_cachep, SLAB_KERNEL);
+
+ if (!recv_pkt) {
+ OMBUG( "Can't allocate receiving packet structure\n");
+ return -ENOMEM;
+ }
+
+ iov.iov_base = recv_pkt;
iov.iov_len = sizeof(struct kcom_pkt);
OMDEBUG_PROTOCOL(2, "KCOMD: reading headers ... \n");
@@ -377,33 +394,42 @@
i = sock_recvmsg(sock, &msg, iov.iov_len, msg.msg_flags);
if ((i == -ENOSPC) || (i == -EAGAIN)) {
+ if (first_loop && iov.iov_len == sizeof(struct kcom_pkt))
+ return i;
+
/* prevent infinite loop */
- if (60000 > retry) goto receive_timeout;
+ if (60000 > ++retry)
+ goto receive_timeout;
+ first_loop = 0;
schedule_timeout(HZ/1000);
goto receive_fragment;
}
+ first_loop = 0;
+
if (i < 0) {
OMBUG("error %d receiving header.\n", i);
+ err = i;
goto receive_error;
}
iov.iov_base += i;
/* sock_revmsg update the iov struct, and len */
- if (iov.iov_len > 0) goto receive_fragment;
-
-
- if (iov.iov_len != 0) goto receive_incomplete;
+ if (iov.iov_len > 0)
+ goto receive_fragment;
+ if (iov.iov_len != 0)
+ goto receive_incomplete;
set_fs(oldfs);
- return recv_kcom_pkt;
-
+ *recv_kcom_pkt = recv_pkt;
+ return 0;
receive_timeout:
OMBUG("Can't receive header %d fragment, too many (%d) retries\n", i, retry);
+ err = -ETIMEDOUT;
goto exit_error;
receive_error:
@@ -415,9 +441,9 @@
exit_error:
- kcom_pkt_delete(recv_kcom_pkt);
+ kcom_pkt_delete(recv_pkt);
set_fs(oldfs);
- return NULL;
+ return err;
}
EXPORT_SYMBOL_GPL(pkt_hdr_read);
@@ -624,6 +650,7 @@
}
INIT_LIST_HEAD(&node->list);
INIT_LIST_HEAD(&node->tasks);
+ INIT_LIST_HEAD(&node->process_list);
rwlock_init(&node->tasks_lock);
@@ -646,6 +673,25 @@
EXPORT_SYMBOL_GPL(kcom_node_add);
/**
+ * __kcom_node_del
+ *
+ * Description:
+ * Delete a node and close its socket
+ **/
+
+void __kcom_node_del(struct kcom_node *node)
+{
+ write_lock(&kcom_nodes_lock);
+ list_del(&node->list);
+ write_unlock(&kcom_nodes_lock);
+
+ /* release and free structure */
+ sys_close(node->fd);
+ sock_release(node->sock);
+ kfree(node);
+}
+
+/**
* kcom_node_del
*
* Description:
@@ -658,23 +704,41 @@
/* remove the node from the list */
node = __kcom_node_find(addr);
-
- write_lock(&kcom_nodes_lock);
- if (!node) {
- write_unlock(&kcom_nodes_lock);
+ if (!node)
return -ENOENT;
- }
- list_del(&node->list);
- write_unlock(&kcom_nodes_lock);
- /* release and free structure */
- sys_close(node->fd);
- sock_release(node->sock);
- kfree(node);
+ __kcom_node_del(node);
+
return 0;
}
/**
+ * kcom_node_sock_release
+ *
+ * Description:
+ * Remove the node (using kcom_node_del) if the task list
+ * is empty, just reset the connection if the tasklist is
+ * not empty... This function hold lock, so don't call it
+ * with kcom_nodes_lock down
+ **/
+
+void kcom_node_sock_release(struct kcom_node *node)
+{
+
+ if (!list_empty(&node->tasks)) {
+ printk(KERN_ERR "Resetting connection\n");
+ write_lock( &kcom_nodes_lock);
+ sock_release(node->sock);
+ node->sock = NULL;
+ node->fd = 0;
+ write_unlock(&kcom_nodes_lock);
+ } else {
+ printk(KERN_ERR "Killing connection\n");
+ __kcom_node_del(node);
+ }
+}
+
+/**
* set_sockopts
*
* Description:
@@ -738,26 +802,24 @@
}
/**
- * create_connection
+ * __create_connection
*
- * Description:
- * creates the network connection to other nodes and creates
- * a node that holds this connection. Stores the corresponding
- * IP address in the node as well.
+ * Description
+ * This function creates the connection and stores it
+ * in the node node. If the node is NULL, the node is
+ * allocated This function may schedule and may hold
+ * the kcom_nodes_lock
**/
-struct kcom_node *create_connection(struct sockaddr *saddr)
-{
+struct kcom_node *__create_connection(struct sockaddr *saddr
+ ,struct kcom_node *node)
+{
struct socket *sock;
- // int fd;
int ret;
int error;
DECLARE_WAITQUEUE(wait, current);
unsigned long timo=MAX_SCHEDULE_TIMEOUT;
- struct kcom_node *node;
-
-
OMDEBUG_PROTOCOL(1, "Creating new connection\n");
ret = sock_create(saddr->sa_family, SOCK_STREAM, IPPROTO_TCP, &sock);
@@ -787,7 +849,6 @@
return NULL;
}
- node = kcom_node_find(saddr);
if (!node) {
node = kcom_node_add(sock);
if (!node) {
@@ -800,10 +861,33 @@
OMBUG("Unable to set socket options.\n");
return NULL;
}
- } else
- printk(KERN_WARNING "Connection already exists. Not creating new node.\n");
+ } else {
+ printk(KERN_WARNING "Connection already exists. (reseting)\n");
+ node->sock = sock;
+ }
return node;
+}
+
+/**
+ * create_connection
+ *
+ * Description:
+ * First tries to find an existing connection, (the socket must
+ * be valid). If no connection is found, (or if the sock is NULL)
+ * a new connection is then created. (there should be only one
+ * connection per saddr) This function may schedule and may hold
+ * the kcom_nodes_lock
+ **/
+struct kcom_node *create_connection(struct sockaddr *saddr)
+{
+
+ struct kcom_node *node;
+
+ node = kcom_node_find(saddr);
+ if (node && node->sock) return node;
+
+ return __create_connection(saddr, node);
}
EXPORT_SYMBOL_GPL(create_connection);
@@ -1053,7 +1137,7 @@
if (!tsk)
return -ENODEV;
- OMDEBUG_PROTOCOL(2, "sending task packet (type=0x%x, datasize=%d)"
+ OMDEBUG_PROTOCOL(2, "sending task packet (type=0x%x, datasize=%d)\n"
,(unsigned)type, datasize);
/* put pkt in kcom_task */
@@ -1462,7 +1546,7 @@
unsigned int msgid;
int i=-1;
- OMDEBUG_PROTOCOL(1, "Sending packet with response (type=0x%x, datasize=%d)"
+ OMDEBUG_PROTOCOL(1, "Sending packet with response (type=0x%x, datasize=%d)\n"
,(unsigned)type, datasize);
node=kcom_node_find((struct sockaddr *)saddr);
Index: linux/hpc/kcomd.c
===================================================================
--- linux.orig/hpc/kcomd.c 2006-11-20 20:26:04.000000000 +0100
+++ linux/hpc/kcomd.c 2006-11-23 18:42:53.000000000 +0100
@@ -328,43 +328,87 @@
}
/**
- * pkt_read
*
- * Description:
- * Reads the packet header and, if exists, data, and put in appropriate task's
- * in_pack list.
- * All but 3 pkts an be handled by the task itself.
- * MIG_INIT creates a new process and task
- * MIG_GO/COME_HOME - migration command.
+ * __pkt_read
+ *
+ * Description
+ * Reads the packet header and, if exists, data,
+ * Does the poorman job of receiving data itself,
+ * if the node didn't hold a valid sock, __pkt_read
+ * creates a new one
**/
-int pkt_read(struct kcom_node *node)
+
+int __pkt_read(struct kcom_node *node, struct kcom_pkt **recv_kcom_pkt)
{
- struct kcom_pkt *recv_kcom_pkt;
- int i=0;
+ int len;
+ int i;
+
+ /* In case of error, sockets may be deleted,
+ * here we try to recover from this situation
+ */
+ if (!node->sock)
+ __create_connection(&node->addr, node);
+ if (!node->sock)
+ return -ENODEV;
OMDEBUG_PROTOCOL(2, "KCOMD: Receiving packet \n");
// read in hdr
- recv_kcom_pkt = pkt_hdr_read(node);
+ i = pkt_hdr_read(node, recv_kcom_pkt);
- if (recv_kcom_pkt==NULL) goto error_recv;
+ if (i<0)
+ return i;
+
+ if(NULL == *recv_kcom_pkt)
+ return -ENOENT;
// read in any data
- if (recv_kcom_pkt->len > 0) {
- if ((recv_kcom_pkt->type & MSG_MASK) == PKT_NEW_MSG) {
+ len = (*recv_kcom_pkt)->len;
+ if (len > 0) {
+ if (((*recv_kcom_pkt)->type & MSG_MASK) == PKT_NEW_MSG) {
+
+ (*recv_kcom_pkt)->data = kzalloc(len, GFP_KERNEL);
- recv_kcom_pkt->data = kzalloc(recv_kcom_pkt->len, GFP_KERNEL);
- i = pkt_data_read(node, recv_kcom_pkt, recv_kcom_pkt->len, recv_kcom_pkt->data);
+ i = pkt_data_read(node, *recv_kcom_pkt, len, (*recv_kcom_pkt)->data);
} else {
- i = pkt_data_read(node, recv_kcom_pkt, recv_kcom_pkt->len, recv_kcom_pkt->resp);
+ i = pkt_data_read(node, *recv_kcom_pkt, len, (*recv_kcom_pkt)->resp);
}
- if (i < recv_kcom_pkt->len) {
+ if (i < len) {
OMBUG("ERROR: incomplete data pkt\n");
- goto error_recv;
+ goto error_delete_packet;
}
}
+ return 0;
+
+error_delete_packet:
+ kcom_pkt_delete(*recv_kcom_pkt);
+ *recv_kcom_pkt = NULL;
+
+ return -ENOENT;
+}
+
+/**
+ * pkt_read
+ *
+ * Description:
+ * and put in appropriate task's
+ * in_pack list.
+ * All but 3 pkts an be handled by the task itself.
+ * MIG_INIT creates a new process and task
+ * MIG_GO/COME_HOME - migration command.
+ **/
+int pkt_read(struct kcom_node *node)
+{
+ struct kcom_pkt *recv_kcom_pkt;
+ int i = 0;
+
+ i = __pkt_read( node, &recv_kcom_pkt);
+
+ if (i<0)
+ return i;
+
OMDEBUG_PROTOCOL_DO(3, om_dump_packet(recv_kcom_pkt));
@@ -385,7 +429,8 @@
sltsk = find_task_by_pid(recv_kcom_pkt->rpid);
if (!sltsk) {
OMBUG("openMosix: Unable to find the task %d\n", recv_kcom_pkt->rpid);
- goto error_recv;
+ kcom_pkt_delete(recv_kcom_pkt);
+ return -ENODEV;
}
task_register_migration(sltsk);
break;
@@ -400,18 +445,6 @@
return 0;
-error_recv:
- /*sys_close(node->fd); */
- sock_release(node->sock);
-
- write_lock(&kcom_nodes_lock);
- list_del(&node->list);
- write_unlock(&kcom_nodes_lock);
- kmem_cache_free(kcom_node_cachep, node);
-
-
- return -1;
-
}
@@ -429,6 +462,12 @@
struct kcom_task *task, *task_next;
struct kcom_pkt *pkt, *pkt_next;
+ if (!node->sock)
+ __create_connection(&node->addr, node);
+
+ if(!node->sock)
+ return -ENODEV;
+
read_lock(&node->tasks_lock);
list_for_each_entry_safe(task, task_next, &node->tasks, list) {
@@ -559,6 +598,91 @@
/**
+ * kcomd_thread_handle_streams:
+ *
+ * Description:
+ * kcomd_thread_handle_streams handle the read/write on the opened sockets
+ * it is uniquely called from kcomd_thread (and should be called without
+ * holding any locks). This function may schedule or sleep.
+ **/
+
+static void kcomd_thread_handle_streams(void)
+{
+
+ struct kcom_node *node, *node_next;
+ struct list_head process_list;
+ int nb_retries = 0;
+ int err = 0;
+
+ INIT_LIST_HEAD(&process_list);
+
+ /* for each nodes { test bit, in, out and do stuff } */
+ /*
+ * If we need to read from a socket, we might schedule or sleep ...
+ * this we must not hold any locks, therefore we must build
+ * a second list for reading ...
+ */
+
+ read_lock(&kcom_nodes_lock);
+ list_for_each_entry (node, &kcom_nodes, list) {
+
+ INIT_LIST_HEAD(&node->process_list);
+
+ if (test_bit(node->fd, sockets_fds.res_in)) {
+ OMDEBUG_KCOMD( 3, "KCOMD: receiving on fd %d\n", node->fd );
+ list_add_tail( &node->process_list, &process_list );
+ if (err!=0) {
+ printk(KERN_ERR "ERROR receiving data => ignoring packet.\n");
+ continue;
+ }
+ }
+ if (node->fd!=-1 && test_bit(node->fd, sockets_fds.res_out)) {
+ data_write(node);
+ }
+ }
+ read_unlock(&kcom_nodes_lock);
+
+ /* Once we have built our read list, we must now use it */
+
+do_read_process_list:
+
+ list_for_each_entry_safe(node, node_next, &process_list, process_list) {
+
+ err=pkt_read(node);
+
+ if (-ENOSPC == err|| -EAGAIN == err)
+ continue;
+
+ list_del(&node->process_list);
+ INIT_LIST_HEAD(&node->process_list);
+
+ /* If we got an error that far ... we must kill the offending connection */
+ if (err < 0)
+ kcom_node_sock_release(node);
+
+ }
+ if (!list_empty(&process_list)) {
+ /* Prevent infinite loop 60s */
+ if (60000 < nb_retries++) {
+ OMBUG("too many retries\n");
+ goto read_clear_list_exit;
+ }
+ schedule_timeout(HZ/1000);
+ goto do_read_process_list;
+ }
+
+ /* Clear the process_list and intialize each element, if we find an
+ * offensive node in it, just delete the socket ... */
+
+read_clear_list_exit:
+ list_for_each_entry_safe(node, node_next, &process_list, process_list) {
+ list_del( &node->process_list);
+ kcom_node_del(&node->addr);
+ }
+
+}
+
+/**
* kcomd_thread
*
* Description:
@@ -574,11 +698,10 @@
static int kcomd_thread(void *nothing)
{
int ret;
- struct kcom_node *node, *node_next;
+ struct kcom_node *node;
s64 timeout = -1;
siginfo_t info; /* matt*/
int sig;
- int err;
struct timeval;
fd4=-1;
@@ -674,24 +797,7 @@
continue;
}
- /* for each nodes { test bit, in, out and do stuff } */
-
- write_lock(&kcom_nodes_lock);
- list_for_each_entry_safe(node, node_next, &kcom_nodes, list) {
-
- if (test_bit(node->fd, sockets_fds.res_in)) {
- OMDEBUG_KCOMD( 3, "KCOMD: receiving on fd %d\n", node->fd );
- err=pkt_read(node);
- if (err!=0) {
- printk(KERN_ERR "ERROR receiving data => ignoring packet.\n");
- continue;
- }
- }
- if (node->fd!=-1 && test_bit(node->fd, sockets_fds.res_out)) {
- data_write(node);
- }
- }
- write_unlock(&kcom_nodes_lock);
+ kcomd_thread_handle_streams();
}
Index: linux/include/hpc/prototype.h
===================================================================
--- linux.orig/include/hpc/prototype.h 2006-11-21 16:23:24.000000000 +0100
+++ linux/include/hpc/prototype.h 2006-11-21 16:24:11.000000000 +0100
@@ -118,7 +118,7 @@
int user_thread(int (*fn)(void *), void * arg, unsigned long flags);
int mig_init(struct kcom_node *,struct kcom_pkt *);
-struct kcom_pkt* pkt_hdr_read(struct kcom_node *);
+int pkt_hdr_read(struct kcom_node *, struct kcom_pkt **);
int pkt_data_read(struct kcom_node *, struct kcom_pkt *, int, char *);
struct kcom_task *kcom_remote_task_find(int);