]> sourceware.org Git - dm.git/commitdiff
Timeout event implementation:
authorBenjamin Marzinski <bmarzins@redhat.com>
Thu, 9 Jun 2005 18:40:49 +0000 (18:40 +0000)
committerBenjamin Marzinski <bmarzins@redhat.com>
Thu, 9 Jun 2005 18:40:49 +0000 (18:40 +0000)
The daemon side of this is mostly the same as the patch I sent out.  To select
a timeout period different than the default and to get the timeout period,
I added two library calls, dm_set_event_timeout() and dm_get_event_timeout().
If people are against them, the other option is to tack extra arguments onto
dm_regiser_for_event() and dm_get_registered_device().  I also added a
-t option to dmevent, so people can try out timeouts.

dmeventd/dmevent.c
lib/event/.exported_symbols
lib/event/dmeventd.c
lib/event/libdm-event.c
lib/libdm-event.h

index 0874c8bd914fe425b0c3274d18457cd62ba66901..9ea63d6ab892eaa51944814b5531a6cb8a588441 100644 (file)
@@ -30,6 +30,7 @@
 static enum event_type events = ALL_ERRORS; /* All until we can distinguish. */
 static char default_dso_name[] = "noop";  /* default DSO is noop */
 static int default_reg = 1;             /* default action is register */
+static uint32_t timeout;
 
 /* Display help. */
 static void print_usage(char *name)
@@ -45,6 +46,7 @@ static void print_usage(char *name)
               "  -h                 Print this usage.\n"
               "  -l                 List registered devices.\n"
               "  -r                 Register for event (default).\n"
+              "  -t <timeout>       (un)register for timeout event.\n"
               "  -u                 Unregister for event.\n"
               "\n", cmd);
 }
@@ -54,7 +56,7 @@ static int parse_argv(int argc, char **argv, char **dso_name_arg,
                      char **device_arg, int *reg, int *list)
 {
        int c;
-       const char *options = "d:hlru";
+       const char *options = "d:hlrt:u";
 
        while ((c = getopt(argc, argv, options)) != -1) {
                switch (c) {
@@ -70,6 +72,14 @@ static int parse_argv(int argc, char **argv, char **dso_name_arg,
                case 'r':
                        *reg = 1;
                        break;
+               case 't':
+                       events = TIMEOUT;
+                       if (sscanf(optarg, "%"SCNu32, &timeout) != 1){
+                               fprintf(stderr, "invalid timeout '%s'\n",
+                                       optarg);
+                               timeout = 0;
+                       }
+                       break;
                case 'u':
                        *reg = 0;
                        break;
@@ -119,19 +129,25 @@ int main(int argc, char **argv)
        multilog_init_verbose(standard, _LOG_DEBUG);
 
        if (list) {
-               do {
-                       if (!(ret= dm_get_registered_device(&dso_name,
-                                                           &device,
-                                                           &events, next))) {
-                               printf("%s %s 0x%x\n",
-                                      dso_name, device, events);
-
-                               if (device_arg)
-                                       break;
-
-                               next = 1;
-                       }
-               } while (!ret);
+               while (1) {
+                       if ((ret= dm_get_registered_device(&dso_name, &device,
+                                                          &events, next)))
+                               break;
+                       printf("%s %s 0x%x", dso_name, device, events);
+                       if (events & TIMEOUT){
+                               if ((ret = dm_get_event_timeout(device,
+                                                               &timeout))) {
+                                       ret = EXIT_FAILURE;
+                                       goto out;
+                               }
+                               printf(" %"PRIu32"\n", timeout);
+                       } else
+                               printf("\n");
+                       if (device_arg)
+                               break;
+
+                       next = 1;
+               }
 
                ret = (ret && device_arg) ? EXIT_FAILURE : EXIT_SUCCESS;
                goto out;
@@ -143,9 +159,16 @@ int main(int argc, char **argv)
                        reg ? "": "un", device, strerror(-ret));
                ret = EXIT_FAILURE;
        } else {
-               printf("%s %sregistered successfully.\n",
-                      device, reg ? "" : "un");
-               ret = EXIT_SUCCESS;
+               if (reg && (events & TIMEOUT) &&
+                   ((ret = dm_set_event_timeout(device, timeout)))){
+                       fprintf(stderr, "Failed to set timeout for %s: %s\n",
+                               device, strerror(-ret));
+                       ret = EXIT_FAILURE;
+               } else {
+                       printf("%s %sregistered successfully.\n",
+                              device, reg ? "" : "un");
+                       ret = EXIT_SUCCESS;
+               }
        }
 
    out:
index 2ff74ee2da82e48fc60af7a90e5a65ce14ea8936..7847409d23d97cab11e0fdf4c76440aa5fef4693 100644 (file)
@@ -1,3 +1,5 @@
 dm_register_for_event
 dm_unregister_for_event
 dm_get_registered_device
+dm_set_event_timeout
+dm_get_event_timeout
index 8d82366dee3ed54ec2fd73391801c9ec343fb2fc..ddacbb807cbdbffa9bbbaf41b9738b3f60db46cf 100644 (file)
@@ -40,7 +40,7 @@
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <unistd.h>
-
+#include <stdarg.h>
 
 
 #define        dbg_malloc(x...)        malloc(x)
@@ -108,6 +108,10 @@ struct message_data {
                char *str;      /* Events string as fetched from message. */
                enum event_type field;  /* Events bitfield. */
        } events;
+       union {
+               char *str;
+               uint32_t secs;
+       } timeout;
        struct daemon_message *msg;     /* Pointer to message buffer. */
 };
 
@@ -129,9 +133,17 @@ struct thread_status {
        enum event_type events; /* bitfield for event filter. */
        enum event_type current_events;/* bitfield for occured events. */
        enum event_type processed_events;/* bitfield for processed events. */
+       time_t next_time;
+       uint32_t timeout;
+       struct list timeout_list;
 };
 static LIST_INIT(thread_registry);
 
+static int timeout_running;
+static LIST_INIT(timeout_registry);
+static pthread_mutex_t timeout_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t timeout_cond = PTHREAD_COND_INITIALIZER;
+
 /* Allocate/free the status structure for a monitoring thread. */
 static struct thread_status *alloc_thread_status(struct message_data *data,
                                                 struct dso_data *dso_data)
@@ -146,6 +158,8 @@ static struct thread_status *alloc_thread_status(struct message_data *data,
                } else {
                        ret->dso_data = dso_data;
                        ret->events   = data->events.field;
+                       ret->timeout  = data->timeout.secs;
+                       list_init(&ret->timeout_list);
                }
        }
 
@@ -242,7 +256,8 @@ static int parse_message(struct message_data *message_data)
         */
        if (fetch_string(&message_data->dso_name, &p) &&
            fetch_string(&message_data->device_path, &p) &&
-           fetch_string(&message_data->events.str, &p)) {
+           fetch_string(&message_data->events.str, &p) &&
+           fetch_string(&message_data->timeout.str, &p)) {
                if (message_data->events.str) {
                        enum event_type i = atoi(message_data->events.str);
 
@@ -253,6 +268,12 @@ static int parse_message(struct message_data *message_data)
                        dbg_free(message_data->events.str);
                        message_data->events.field = i;
                }
+               if (message_data->timeout.str) {
+                       uint32_t secs = atoi(message_data->timeout.str);
+                       dbg_free(message_data->timeout.str);
+                       message_data->timeout.secs = secs ? secs :
+                                                           DEFAULT_TIMEOUT;
+               }
 
                return 1;
        }
@@ -344,9 +365,115 @@ static int error_detected(struct thread_status *thread, char *params)
        return 0;
 }
 
+static void exit_timeout(void *unused)
+{
+       timeout_running = 0;
+       pthread_mutex_unlock(&timeout_mutex);
+}
+
+/* wake up monitor threads every so often */
+static void *timeout_thread(void *unused)
+{
+       struct timespec timeout;
+       time_t curr_time;
+
+       timeout.tv_nsec = 0;
+       pthread_cleanup_push(exit_timeout, NULL);
+       pthread_mutex_lock(&timeout_mutex);
+       while(1) {
+               struct thread_status *thread;
+               timeout.tv_sec = (time_t)-1;
+               curr_time = time(NULL);
+               if (list_empty(&timeout_registry))
+                       break;
+               list_iterate_items_gen(thread, &timeout_registry,
+                                      timeout_list) {
+                       if (thread->next_time < curr_time) {
+                               thread->next_time = curr_time + thread->timeout;
+                               pthread_kill(thread->thread, SIGALRM);
+                       }
+                       if (thread->next_time < timeout.tv_sec)
+                               timeout.tv_sec = thread->next_time;
+               }
+               pthread_cond_timedwait(&timeout_cond, &timeout_mutex, &timeout);
+       }
+       timeout_running = 0;
+       pthread_mutex_unlock(&timeout_mutex);
+       pthread_cleanup_pop(0);
+
+       return NULL;
+}
+
+static int register_for_timeout(struct thread_status *thread)
+{
+       int ret = 0;
+       pthread_mutex_lock(&timeout_mutex);
+       thread->next_time = time(NULL) + thread->timeout;
+       if (list_empty(&thread->timeout_list)) {
+               list_add(&timeout_registry, &thread->timeout_list);
+               if (timeout_running)
+                       pthread_cond_signal(&timeout_cond);
+       }
+       if (!timeout_running) {
+               pthread_t timeout_id;
+               ret = -pthread_create(&timeout_id, NULL, timeout_thread, NULL);
+               if (ret)
+                       goto out;
+               timeout_running = 1;
+       }
+   out:
+       pthread_mutex_unlock(&timeout_mutex);
+       return ret;
+}
+
+static void unregister_for_timeout(struct thread_status *thread)
+{
+       pthread_mutex_lock(&timeout_mutex);
+       if (!list_empty(&thread->timeout_list)) {
+               list_del(&thread->timeout_list);
+               list_init(&thread->timeout_list);
+       }
+       pthread_mutex_unlock(&timeout_mutex);
+}
+
+static void no_intr_log(int level, const char *file, int line,
+                      const char *f, ...)
+{
+       va_list ap;
+
+       if (errno == EINTR)
+               return;
+       if (level > _LOG_WARN)
+               return;
+
+       va_start(ap, f);
+
+       if (level < _LOG_WARN)
+               vfprintf(stderr, f, ap);
+       else
+               vprintf(f, ap);
+
+       va_end(ap);
+
+       if (level < _LOG_WARN)
+               fprintf(stderr, "\n");
+       else
+               fprintf(stdout, "\n");
+}
+
+static sigset_t unblock_sigalrm(void)
+{
+       sigset_t set, old;
+       sigemptyset(&set);
+       sigaddset(&set, SIGALRM);
+       pthread_sigmask(SIG_UNBLOCK, &set, &old);
+       return old;
+}
+
 /* Wait on a device until an event occurs. */
 static int event_wait(struct thread_status *thread)
 {
+       sigset_t set;
        int ret = 0;
 /*
        void *next = NULL;
@@ -359,9 +486,15 @@ static int event_wait(struct thread_status *thread)
        if (!(dmt = dm_task_create(DM_DEVICE_WAITEVENT)))
                return 0;
 
-       if ((ret = dm_task_set_name(dmt, dm_basename(thread->device_path))) &&
-           (ret = dm_task_set_event_nr(dmt, thread->event_nr)) &&
-           (ret = dm_task_run(dmt))) {
+       if (!(ret = dm_task_set_name(dmt, dm_basename(thread->device_path))) ||
+           !(ret = dm_task_set_event_nr(dmt, thread->event_nr)))
+               goto out;
+       /* This is so that you can break out of waiting on an event,
+          either for a timeout event, or to cancel the thread */
+       set = unblock_sigalrm();
+       dm_log_init(no_intr_log);
+       errno = 0;
+       if ((ret = dm_task_run(dmt))) {
 /*
                do {
                        params = NULL;
@@ -386,8 +519,15 @@ static int event_wait(struct thread_status *thread)
 
                if ((ret = dm_task_get_info(dmt, &info)))
                        thread->event_nr = info.event_nr;
+       } else if (thread->events & TIMEOUT && errno == EINTR) {
+               thread->current_events |= TIMEOUT;
+               ret = 1;
+               thread->processed_events = 0;
        }
+       pthread_sigmask(SIG_SETMASK, &set, NULL);
+       dm_log_init(NULL);
 
+   out:
        dm_task_destroy(dmt);
 
        return ret;
@@ -469,7 +609,11 @@ static int create_thread(struct thread_status *thread)
 
 static int terminate_thread(struct thread_status *thread)
 {
-       return pthread_cancel(thread->thread);
+       int ret;
+       if ((ret = pthread_cancel(thread->thread)))
+               return ret;
+       ret = pthread_kill(thread->thread, SIGALRM);
+       return ret;
 }
 
 /* DSO reference counting. */
@@ -600,7 +744,7 @@ static struct dso_data *load_dso(struct message_data *data)
 static int register_for_event(struct message_data *message_data)
 {
        int ret = 0;
-       struct thread_status *thread, *thread_new;
+       struct thread_status *thread, *thread_new = NULL;
        struct dso_data *dso_data;
 
        if (!device_exists(message_data->device_path)) {
@@ -642,6 +786,7 @@ static int register_for_event(struct message_data *message_data)
                lock_mutex();
                if ((ret = -create_thread(thread))) {
                        unlock_mutex();
+                       do_unregister_device(thread);
                        free_thread_status(thread);
                        goto out;
                } else
@@ -652,7 +797,16 @@ static int register_for_event(struct message_data *message_data)
        thread->events |= message_data->events.field;
 
        unlock_mutex();
+       /* FIXME - If you fail to register for timeout events, you
+          still monitor all the other events. Is this the right
+          action for newly created devices?  Also, you are still
+          on the timeout registry, so if a timeout thread is
+          successfully started up later, you will start receiving
+          TIMEOUT events */
+       if (thread->events & TIMEOUT)
+               ret = -register_for_timeout(thread);
 
+   out:
        /*
         * Deallocate thread status after releasing
         * the lock in case we haven't used it.
@@ -660,7 +814,6 @@ static int register_for_event(struct message_data *message_data)
        if (thread_new)
                free_thread_status(thread_new);
 
-   out:
        free_message(message_data);
 
        return ret;
@@ -690,6 +843,8 @@ static int unregister_for_event(struct message_data *message_data)
 
        thread->events &= ~message_data->events.field;
 
+       if (!(thread->events & TIMEOUT))
+               unregister_for_timeout(thread);
        /*
         * In case there's no events to monitor on this device ->
         * unlink and terminate its monitoring thread.
@@ -795,11 +950,53 @@ static int get_registered_device(struct message_data *message_data, int next)
        }
 
    out:
+       free_message(message_data);
        unlock_mutex();
 
        return -ENOENT;
 }
 
+static int set_timeout(struct message_data *message_data)
+{
+       int ret = 0;
+       struct thread_status *thread;
+
+       lock_mutex();
+       if (!(thread = lookup_thread_status(message_data))) {
+               unlock_mutex();
+               ret = -ENODEV;
+               goto out;
+       }
+       thread->timeout = message_data->timeout.secs; 
+       unlock_mutex();
+
+   out:
+       free_message(message_data);
+
+       return ret;
+}
+
+static int get_timeout(struct message_data *message_data)
+{
+       int ret = 0;
+       struct thread_status *thread;
+       struct daemon_message *msg = message_data->msg;
+
+       lock_mutex();
+       if (!(thread = lookup_thread_status(message_data))) {
+               unlock_mutex();
+               ret = -ENODEV;
+               goto out;
+       }
+       snprintf(msg->msg, sizeof(msg->msg), "%"PRIu32, thread->timeout);
+       unlock_mutex();
+   out:
+       free_message(message_data);
+
+       return ret;
+}
+       
+
 /* Initialize a fifos structure with path names. */
 static int init_fifos(struct fifos *fifos)
 {
@@ -912,6 +1109,12 @@ log_print("%s: %u \"%s\"\n", __func__, msg->opcode.cmd, message_data.msg->msg);
        case CMD_GET_NEXT_REGISTERED_DEVICE:
                ret = get_registered_device(&message_data, 1);
                break;
+       case CMD_SET_TIMEOUT:
+               ret = set_timeout(&message_data);
+               break;
+       case CMD_GET_TIMEOUT:
+               ret = get_timeout(&message_data);
+               break;
        default:
                ret = -EINVAL;
                break;
@@ -941,11 +1144,20 @@ log_print("%s: status: %s\n", __func__, strerror(-msg.opcode.status));
                stack;
 }
 
+static void sig_alarm(int signum)
+{
+       pthread_testcancel();
+}
+
 /* Init thread signal handling. */
 static void init_thread_signals(void)
 {
        sigset_t sigset;
+       struct sigaction act;
        
+       memset(&act, 0, sizeof(act));
+       act.sa_handler = sig_alarm;
+       sigaction(SIGALRM, &act, NULL);
        sigfillset(&sigset);
        pthread_sigmask(SIG_BLOCK, &sigset, NULL);
 }
index 5cacdc76daa59fdfe899e926a37df9594e51eecf..ece0cdaef5b013c186abcccd29d58c9e018fc3e0 100644 (file)
@@ -116,7 +116,7 @@ static int daemon_write(struct fifos *fifos, struct daemon_message *msg)
 
 static int daemon_talk(struct fifos *fifos, struct daemon_message *msg,
                       int cmd, char *dso_name, char *device,
-                      enum event_type events)
+                      enum event_type events, uint32_t timeout)
 {
        memset(msg, 0, sizeof(*msg));
 
@@ -125,8 +125,9 @@ static int daemon_talk(struct fifos *fifos, struct daemon_message *msg,
         * into ASCII message string.
         */
        msg->opcode.cmd = cmd;
-       snprintf(msg->msg, sizeof(msg->msg), "%s %s %u",
-                dso_name ? dso_name : "", device ? device : "", events);
+       snprintf(msg->msg, sizeof(msg->msg), "%s %s %u %"PRIu32,
+                dso_name ? dso_name : "", device ? device : "",
+                events, timeout);
 
        /*
         * Write command and message to and
@@ -296,7 +297,8 @@ static int device_exists(char *device)
 
 /* Handle the event (de)registration call and return negative error codes. */
 static int do_event(int cmd, struct daemon_message *msg,
-                   char *dso_name, char *device, enum event_type events)
+                   char *dso_name, char *device, enum event_type events,
+                   uint32_t timeout)
 {
        int ret;
        struct fifos fifos;
@@ -306,7 +308,7 @@ static int do_event(int cmd, struct daemon_message *msg,
                return -ESRCH;
        }
 
-       ret = daemon_talk(&fifos, msg, cmd, dso_name, device, events);
+       ret = daemon_talk(&fifos, msg, cmd, dso_name, device, events, timeout);
 
        /* what is the opposite of init? */
        dtr_client(&fifos);
@@ -325,7 +327,7 @@ int dm_register_for_event(char *dso_name, char *device_path,
                return -ENODEV;
 
        return do_event(CMD_REGISTER_FOR_EVENT, &msg,
-                       dso_name, device_path, events);
+                       dso_name, device_path, events, 0);
 }
 
 int dm_unregister_for_event(char *dso_name, char *device_path,
@@ -338,7 +340,7 @@ int dm_unregister_for_event(char *dso_name, char *device_path,
                return -ENODEV;
 
        return do_event(CMD_UNREGISTER_FOR_EVENT, &msg,
-                       dso_name, device_path, events);
+                       dso_name, device_path, events, 0);
 }
 
 int dm_get_registered_device(char **dso_name, char **device_path,
@@ -351,7 +353,7 @@ int dm_get_registered_device(char **dso_name, char **device_path,
 
        if (!(ret = do_event(next ? CMD_GET_NEXT_REGISTERED_DEVICE :
                                    CMD_GET_REGISTERED_DEVICE,
-                            &msg, *dso_name, *device_path, *events)))
+                            &msg, *dso_name, *device_path, *events, 0)))
                ret = parse_message(&msg, &dso_name_arg, &device_path_arg,
                                    events);
 
@@ -372,6 +374,27 @@ int dm_get_registered_device(char **dso_name, char **device_path,
        return ret;
 }
 
+int dm_set_event_timeout(char *device_path, uint32_t timeout)
+{
+       struct daemon_message msg;
+
+       if (!device_exists(device_path))
+               return -ENODEV;
+       return do_event(CMD_SET_TIMEOUT, &msg,
+                       NULL, device_path, 0, timeout);
+}
+
+int dm_get_event_timeout(char *device_path, uint32_t *timeout)
+{
+       int ret;
+       struct daemon_message msg;
+
+       if (!device_exists(device_path))
+               return -ENODEV;
+       if (!(ret = do_event(CMD_GET_TIMEOUT, &msg, NULL, device_path, 0, 0)))
+               *timeout = atoi(msg.msg);
+       return ret;
+}
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * Emacs will notice this stuff at the end of the file and automatically
index a257b7f0fd1256547c59cedeef20884ade452a1d..d92eb71e2ccd1ab0142a86a8121166a9972e3cf4 100644 (file)
@@ -25,6 +25,7 @@
 #define        FIFO_SERVER     "/var/run/dmeventd-server"
 #define PIDFILE                "/var/run/dmeventd.pid"
 
+#define DEFAULT_TIMEOUT 10
 /* Commands for the daemon passed in the message below. */
 enum dmeventd_command {
        CMD_ACTIVE = 1,
@@ -32,6 +33,8 @@ enum dmeventd_command {
        CMD_UNREGISTER_FOR_EVENT,
        CMD_GET_REGISTERED_DEVICE,
        CMD_GET_NEXT_REGISTERED_DEVICE,
+       CMD_SET_TIMEOUT,
+       CMD_GET_TIMEOUT,
 };
 
 /* Message passed between client and daemon. */
@@ -60,6 +63,7 @@ enum event_type {
        PATH_ERROR      = 0x10, /* Failure on an io path. */
        ADAPTOR_ERROR   = 0x20, /* Failure off a host adaptor. */
        SYNC_STATUS     = 0x40, /* Mirror synchronization completed/failed. */
+       TIMEOUT         = 0x80, /* Timeout has occured */
 };
 #define        ALL_ERRORS (SECTOR_ERROR | DEVICE_ERROR | PATH_ERROR | ADAPTOR_ERROR)
 
@@ -69,6 +73,8 @@ int dm_unregister_for_event(char *dso_name, char *device,
                            enum event_type events);
 int dm_get_registered_device(char **dso_name, char **device,
                             enum event_type *events, int next);
+int dm_set_event_timeout(char *device, uint32_t timeout);
+int dm_get_event_timeout(char *device, uint32_t *timeout);
 
 /* Prototypes for DSO interface. */
 void process_event(char *device, enum event_type event);
This page took 0.042716 seconds and 5 git commands to generate.