From: Benjamin Marzinski Date: Thu, 9 Jun 2005 18:40:49 +0000 (+0000) Subject: Timeout event implementation: X-Git-Tag: v1_01_03~7 X-Git-Url: https://sourceware.org/git/?a=commitdiff_plain;h=66d8f4718432201cce841766321caa1f5894d0a5;p=dm.git Timeout event implementation: 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. --- diff --git a/dmeventd/dmevent.c b/dmeventd/dmevent.c index 0874c8b..9ea63d6 100644 --- a/dmeventd/dmevent.c +++ b/dmeventd/dmevent.c @@ -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 (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: diff --git a/lib/event/.exported_symbols b/lib/event/.exported_symbols index 2ff74ee..7847409 100644 --- a/lib/event/.exported_symbols +++ b/lib/event/.exported_symbols @@ -1,3 +1,5 @@ dm_register_for_event dm_unregister_for_event dm_get_registered_device +dm_set_event_timeout +dm_get_event_timeout diff --git a/lib/event/dmeventd.c b/lib/event/dmeventd.c index 8d82366..ddacbb8 100644 --- a/lib/event/dmeventd.c +++ b/lib/event/dmeventd.c @@ -40,7 +40,7 @@ #include #include #include - +#include #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); } diff --git a/lib/event/libdm-event.c b/lib/event/libdm-event.c index 5cacdc7..ece0cda 100644 --- a/lib/event/libdm-event.c +++ b/lib/event/libdm-event.c @@ -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 diff --git a/lib/libdm-event.h b/lib/libdm-event.h index a257b7f..d92eb71 100644 --- a/lib/libdm-event.h +++ b/lib/libdm-event.h @@ -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);