[patch 5/8] openmosix/data_write-process-rewrite.patch
Florian Delizy <[email protected]> Thu, 23 Nov 2006 19:39:13 +0100
| Newsgroups | gmane.linux.cluster.openmosix.devel |
|---|---|
| Message-ID | <[email protected]> |
This patch breaks kcomd_thread_handle_streams into separate functions for read and write, and create a task processing list for solving race conditions (as well as error handling) tasks now have two separate lists for output packets: out_packs : where packets are initially added egress_list: where kcomd put temporary packets before they get sent ------------------------------------------------------------------------- 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
data_write-process-rewrite.patch
(text/x-patch, 16.1 KB)
Subject: [patch @num@/@total@] @name@
This patch breaks kcomd_thread_handle_streams into separate functions
for read and write, and create a task processing list for solving
race conditions (as well as error handling)
tasks now have two separate lists for output packets:
out_packs : where packets are initially added
egress_list: where kcomd put temporary packets before they get sent
Index: linux/include/hpc/kcom.h
===================================================================
--- linux.orig/include/hpc/kcom.h 2006-11-21 19:13:11.000000000 +0100
+++ linux/include/hpc/kcom.h 2006-11-21 21:35:24.000000000 +0100
@@ -112,6 +112,7 @@
char *resp; /* ptr of response */
};
+
struct kcom_node
{
int fd; /* fd to send packet */
@@ -139,6 +140,9 @@
rwlock_t out_packs_lock; /* List structure locks ...*/
rwlock_t in_packs_lock;
+
+ struct list_head process_list; /* list used internally by kcomd */
+ struct list_head egress_list; /* list used internally by kcomd */
};
extern int kcom_send_nack(task_t *p, struct kcom_pkt *recv_pkt);
Index: linux/hpc/kcom.c
===================================================================
--- linux.orig/hpc/kcom.c 2006-11-21 19:14:09.000000000 +0100
+++ linux/hpc/kcom.c 2006-11-23 18:42:18.000000000 +0100
@@ -934,6 +934,8 @@
INIT_LIST_HEAD(&kctask->list);
INIT_LIST_HEAD(&kctask->out_packs);
INIT_LIST_HEAD(&kctask->in_packs);
+ INIT_LIST_HEAD(&kctask->process_list);
+ INIT_LIST_HEAD(&kctask->egress_list);
rwlock_init(&kctask->in_packs_lock);
rwlock_init(&kctask->out_packs_lock);
@@ -1164,6 +1166,43 @@
}
/**
+ * __kcom_find_or_create_task
+ *
+ * Description:
+ * This function attempt to find the task for the corresponding address and pid
+ * if not found, it creates it or return -ENODEV or -ENOMEM in case of problem
+ *
+ * The task is filled in *tsk
+ **/
+
+int __kcom_find_or_create_task(struct sockaddr_in *saddr, struct kcom_task **tsk, pid_t pid)
+{
+ struct kcom_node *node;
+
+ node=kcom_node_find((struct sockaddr *)saddr);
+
+ if (!node) {
+
+ node=create_connection((struct sockaddr *)saddr);
+ if (!node) {
+ OMBUG("ERROR: Unable to create new connection.\n");
+ return -ENODEV;
+ }
+ }
+
+ *tsk=kcom_task_find(pid);
+ if (!*tsk) {
+ *tsk=kcom_task_create(node, pid);
+ if (!*tsk) {
+ OMBUG("ERROR: Unable to create task.\n");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+/**
* kcom_send
*
* Description:
@@ -1176,29 +1215,16 @@
int kcom_send(int type, int datasize, char *data, unsigned long addr, struct sockaddr_in *saddr)
{
- struct kcom_node *node;
struct kcom_task *tsk;
- task_t *p = current;
unsigned int msgid;
+ int ret;
OMDEBUG_PROTOCOL(1, "protocol: send packet (type=0x%x datasize=%d)\n"
,(unsigned)type, datasize);
- node=kcom_node_find((struct sockaddr *)saddr);
- if (node==NULL) {
- OMBUG("node not found, creating new connection.\n");
- node=create_connection((struct sockaddr *)saddr);
- if (node==NULL)
- return -1;
- }
-
- tsk=kcom_task_find(p->pid);
- if (tsk==NULL) {
- OMBUG("Task not found. creating new.\n");
- tsk=kcom_task_create(node, p->pid);
- if (tsk==NULL)
- return -1;
- }
+ ret = __kcom_find_or_create_task(saddr, &tsk, current->pid);
+ if (0!=ret)
+ return ret;
msgid=kcom_task_send(tsk, type, datasize, data, NULL, addr);
@@ -1466,6 +1492,7 @@
}
+
/**
* kcom_send_with_ack
*
@@ -1474,34 +1501,17 @@
**/
int kcom_send_with_ack(int type, int datasize, char *data, unsigned long addr, struct sockaddr_in *saddr)
{
- struct kcom_node *node;
- struct kcom_task *tsk;
- task_t *p = current;
+ struct kcom_task *tsk = NULL;
unsigned int msgid;
+ int ret;
int ack;
OMDEBUG_PROTOCOL(2, "sending packet with ack (type=0x%x, datasize=%d)\n"
,(unsigned)type, datasize);
- node=kcom_node_find((struct sockaddr *)saddr);
-
- if (node==NULL) {
-
- node=create_connection((struct sockaddr *)saddr);
- if (!node) {
- OMBUG("ERROR: Unable to create new connection.\n");
- goto return_error;
- }
- }
-
- tsk=kcom_task_find(p->pid);
- if (tsk==NULL) {
- tsk=kcom_task_create(node, p->pid);
- if (!tsk) {
- OMBUG("ERROR: Unable to create task.\n");
- goto return_error;
- }
- }
+ ret = __kcom_find_or_create_task(saddr, &tsk, current->pid);
+ if (0!=ret)
+ return ret;
// FIXME: what if tsk is deleted between kcom_task_send and wait_for_ack ?
msgid = kcom_task_send(tsk, type, datasize, data, NULL, addr);
@@ -1510,7 +1520,7 @@
send_sig(SIGHUP, kcomd_task, 0);
else {
OMBUG("Unable to find kcomd daemon.\n");
- goto return_error;
+ return -ENODEV;
}
set_current_state(TASK_INTERRUPTIBLE);
@@ -1524,12 +1534,10 @@
if (ack == -2) {
OMBUG("Got NACK !! \n");
- goto return_error;
+ return -1;
}
return 0;
-return_error:
- return -1;
}
/**
@@ -1540,31 +1548,17 @@
**/
int kcom_send_with_response(int type, int datasize, char *data, unsigned long addr, char *resp, struct sockaddr_in *saddr)
{
- struct kcom_node *node;
struct kcom_task *tsk;
- task_t *p = current;
unsigned int msgid;
int i=-1;
+ int ret;
OMDEBUG_PROTOCOL(1, "Sending packet with response (type=0x%x, datasize=%d)\n"
,(unsigned)type, datasize);
- node=kcom_node_find((struct sockaddr *)saddr);
- if (node==NULL) {
- node=create_connection((struct sockaddr *)saddr);
- if (node==NULL) {
- return -1;
- }
- }
-
- tsk=kcom_task_find(p->pid);
- if (!tsk) {
- tsk=kcom_task_create(node, p->pid);
- if (!tsk) {
- OMBUG("Unable to create task\n");
- return -1;
- }
- }
+ ret = __kcom_find_or_create_task(saddr, &tsk, current->pid);
+ if (0!=ret)
+ return ret;
msgid=kcom_task_send(tsk, type, datasize, data, resp, addr);
Index: linux/hpc/kcomd.c
===================================================================
--- linux.orig/hpc/kcomd.c 2006-11-21 19:16:41.000000000 +0100
+++ linux/hpc/kcomd.c 2006-11-23 18:42:26.000000000 +0100
@@ -159,6 +159,9 @@
*
* Description:
* Sends the kcom pkt header and the data, if any.
+ * return 0 on success, a negative value if not
+ *
+ * This function may schedule
**/
int data_send(struct socket *sock, struct kcom_pkt* pkt)
@@ -171,6 +174,8 @@
int first_vec = 0;
int sent = 0;
int i;
+ int first_loop = 1;
+ int nb_retries = 0;
/* Sanity check */
@@ -179,6 +184,11 @@
return -EFAULT;
}
+ OMDEBUG_KCOMD(3, "KCOMD: send packet type=0x%x len=%d\n"
+ ,(unsigned)pkt->type, pkt->len);
+
+ OMDEBUG_KCOMD_DO(4, om_dump_packet(pkt));
+
/* Prepare packets */
msg.msg_flags = MSG_WAITALL | MSG_NOSIGNAL;
@@ -206,10 +216,22 @@
/* kernel_sendmsg can fail if no space left on skbuff, or interrupted ... */
if (-ENOSPC == sent || -EAGAIN == sent || 0 == sent) {
- OMBUG("Can not send packet for now, retrying\n");
+
+ if (first_loop && total_data == packet[0].iov_len + packet[1].iov_len)
+ return -EAGAIN;
+
+ first_loop = 0;
+
+ if (60000 < ++nb_retries)
+ return -ETIMEDOUT;
+
+ if (0==nb_retries%1000)
+ printk(KERN_WARNING"Can not send packet for now, retrying\n");
+
schedule_timeout(HZ/1000);
continue;
}
+ first_loop = 0;
/* if something wrong happened :*/
if (0 > sent) {
@@ -221,7 +243,9 @@
total_data -= sent;
- if (!total_data) break;
+ if (!total_data)
+ break;
+
/* Thanks to fs/cifs/transport.c !*/
for (i = first_vec; i < 2; i++) {
@@ -242,7 +266,7 @@
}
- return pkt->len;
+ return 0;
}
@@ -447,47 +471,6 @@
}
-
-
-/**
- * data_write
- *
- * Description:
- * Loops through all tasks that have processes on the node that
- * has data to send, and sends the pkts.
- * Once the pkt has been sent, its memory is freed.
- **/
-int data_write(struct kcom_node *node)
-{
- 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) {
-
- write_lock(&task->out_packs_lock);
- list_for_each_entry_safe(pkt, pkt_next, &task->out_packs, list) {
-
- OMDEBUG_KCOMD(3, "KCOMD: send packet type=0x%x len=%d\n"
- ,(unsigned)pkt->type, pkt->len);
-
- OMDEBUG_KCOMD_DO(4, om_dump_packet(pkt));
- data_send(node->sock, pkt);
-
- kcom_pkt_delete(pkt);
- }
- write_unlock(&task->out_packs_lock);
- }
- read_unlock(&node->tasks_lock);
- return 0;
-}
-
static void kcomd_thread_free_ressources(void);
/**
@@ -598,55 +581,24 @@
/**
- * kcomd_thread_handle_streams:
+ * __kcomd_thread_do_read
*
* 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)
+ * this function (only called from kcomd_thread_handle_streams) is responsible
+ * for reading packets out of the process_list
+ *
+ * This function may schedule or hold locks
+ */
+void __kcomd_thread_do_read(struct list_head* process_list)
{
-
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:
+do_process_list:
- list_for_each_entry_safe(node, node_next, &process_list, process_list) {
+ list_for_each_entry_safe(node, node_next, process_list, process_list) {
err=pkt_read(node);
@@ -660,29 +612,217 @@
if (err < 0) {
kcom_node_increment_error(node);
kcom_node_sock_release(node);
+ printk(KERN_ERR "ERROR receiving data => ignoring packet.\n");
}
kcom_node_clear_error(node);
}
- if (!list_empty(&process_list)) {
+ if (!list_empty(process_list)) {
/* Prevent infinite loop 60s */
- if (60000 < nb_retries++) {
+ if (60000 < ++nb_retries) {
OMBUG("too many retries\n");
- goto read_clear_list_exit;
+ goto clear_list_exit;
}
schedule_timeout(HZ/1000);
- goto do_read_process_list;
+ goto do_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) {
+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);
+ INIT_LIST_HEAD(&node->process_list);
+ kcom_node_increment_error(node);
}
+}
+
+/**
+ * __kcomd_thread_do_write
+ *
+ * Description:
+ * only called from kcomd_thread_handle_streams, read the list, and write
+ * all pending packets. Handle the error count of nodes as well
+ *
+ * This function may schedule or hold locks ...
+ **/
+
+void __kcomd_thread_do_write(struct list_head* process_list)
+{
+ struct kcom_task* task, *task_next;
+ struct kcom_pkt* pkt, *pkt_next;
+ struct kcom_node* node = NULL;
+ int err = 0;
+ int nb_retries = 0;
+ struct socket* sock = NULL;
+
+do_process_list:
+
+ list_for_each_entry_safe(task, task_next, process_list, process_list){
+
+ node = task->node;
+ if (!node)
+ goto next_task_del;
+ /* First check all socks are ok */
+ if(!node->sock)
+ __create_connection(&node->addr, node);
+
+ sock = node->sock;
+
+ if(!sock)
+ goto next_task_error;
+
+ /* Now send all packets on the egress_list */
+
+ list_for_each_entry_safe(pkt, pkt_next, &task->egress_list, list) {
+
+ err = data_send(sock, pkt);
+
+ /* If EAGAIN or ENOSPC, we will wait after trying all packets only*/
+ if (-EAGAIN==err || -ENOSPC==err)
+ goto next_task;
+
+ /* if any other error, reset the offending connection,
+ * and get the task out of the proces_list */
+ if (err<0) {
+ kcom_node_sock_release(node);
+ goto next_task_error;
+ }
+
+ kcom_pkt_delete(pkt);
+ kcom_node_clear_error(node);
+ }
+
+ /* If we arrive here, all packets are sent */
+
+ /* Next task removing the task from process_list */
+ next_task_del:
+ list_del(&task->process_list);
+ INIT_LIST_HEAD(&task->process_list);
+ /* Next task without removing the task from process_list */
+ next_task:
+ continue;
+
+ /* Next task incrementing the error count */
+ next_task_error:
+ list_del(&task->process_list);
+ INIT_LIST_HEAD(&task->process_list);
+ /* This function may delete the node */
+ kcom_node_increment_error(node);
+ /* FIXME: in the future, if the node is deleted,
+ * we should find all occurences of the node
+ * in the process_list to remove relative tasks as well
+ */
+
+ }
+
+ /* We passed once, but we may have been interrupted ... so check */
+
+ if (!list_empty(process_list)) {
+ /* Prevent infinite loop 60s */
+ if (60000 < ++nb_retries) {
+ OMBUG("too many retries\n");
+ goto clear_list_exit;
+ }
+ schedule_timeout(HZ/1000);
+ goto do_process_list;
+ }
+
+clear_list_exit:
+ list_for_each_entry_safe(task, task_next, process_list, process_list) {
+ list_del( &task->process_list);
+ INIT_LIST_HEAD(&node->process_list);
+ kcom_node_increment_error(task->node);
+ }
+}
+
+/*
+ * __kcomd_thread_prepare_task
+ *
+ * Description:
+ * Move all packets from out_packs list to egress_list for sending
+ **/
+
+static void __kcomd_thread_prepare_task(struct kcom_task* task)
+{
+ struct kcom_pkt *pkt, *pkt_next;
+
+ write_lock(&task->out_packs_lock);
+ list_for_each_entry_safe(pkt, pkt_next, &task->out_packs, list){
+ list_move_tail(&pkt->list, &task->egress_list);
+ }
+ write_unlock(&task->out_packs_lock);
+}
+
+/**
+ * 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.
+ *
+ * This function creates two lists, one for write one for read, and use
+ * __kcomd_thread_do_read/__kcomd_thread_do_write to handle the work.
+ **/
+
+void kcomd_thread_handle_streams(void)
+{
+
+ struct kcom_node *node;
+ struct kcom_task *task;
+ struct list_head process_list;
+ struct list_head write_process_list;
+
+ INIT_LIST_HEAD(&process_list);
+ INIT_LIST_HEAD(&write_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);
+
+ /* Build read 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 );
+ }
+
+ /* Build write list */
+ if (node->fd!=-1 && test_bit(node->fd, sockets_fds.res_out)) {
+ OMDEBUG_KCOMD( 3, "KCOMD: sending on fd %d\n", node->fd );
+ read_lock(&node->tasks_lock);
+ list_for_each_entry(task, &node->tasks, list){
+
+ INIT_LIST_HEAD(&task->process_list);
+
+ if (!list_empty(&task->out_packs)
+ || !list_empty(&task->egress_list)) {
+ list_add_tail(&task->process_list, &write_process_list);
+ __kcomd_thread_prepare_task(task);
+ }
+ }
+ read_unlock(&node->tasks_lock);
+ }
+ }
+ read_unlock(&kcom_nodes_lock);
+
+ /* Since the above function may schedule or hold locks, we need
+ * separate lists */
+ if (!list_empty(&process_list))
+ __kcomd_thread_do_read(&process_list);
+
+ if (!list_empty(&write_process_list))
+ __kcomd_thread_do_write(&write_process_list);
}