X-Git-Url: http://git.lttng.org/?a=blobdiff_plain;f=libust%2Flttng-ust-comm.c;h=2e7e1a3515abd1f6b46024364ddb1747057e8a4f;hb=193183fb924aef705f30358e54e7386a3a64c78d;hp=3ee862a00871d267ebd1f4632881256ca6bb0537;hpb=44e073f54f10c398cbe10c702a36915d414e5e62;p=lttng-ust.git diff --git a/libust/lttng-ust-comm.c b/libust/lttng-ust-comm.c index 3ee862a0..2e7e1a35 100644 --- a/libust/lttng-ust-comm.c +++ b/libust/lttng-ust-comm.c @@ -19,11 +19,14 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#define _LGPL_SOURCE #include #include #include #include #include +#include +#include #include #include #include @@ -33,6 +36,7 @@ #include #include #include +#include #include #include @@ -115,6 +119,8 @@ struct sock_info local_apps = { .socket = -1, }; +static int wait_poll_fallback; + extern void ltt_ring_buffer_client_overwrite_init(void); extern void ltt_ring_buffer_client_discard_init(void); extern void ltt_ring_buffer_metadata_client_init(void); @@ -211,6 +217,9 @@ int handle_register_done(struct sock_info *sock_info) if (sock_info->constructor_sem_posted) return 0; sock_info->constructor_sem_posted = 1; + if (uatomic_read(&sem_count) <= 0) { + return 0; + } ret = uatomic_add_return(&sem_count, -1); if (ret == 0) { ret = sem_post(&constructor_wait); @@ -226,6 +235,7 @@ int handle_message(struct sock_info *sock_info, int ret = 0; const struct objd_ops *ops; struct lttcomm_ust_reply lur; + int shm_fd, wait_fd; ust_lock(); @@ -271,10 +281,50 @@ end: if (ret >= 0) { lur.ret_code = LTTCOMM_OK; } else { - lur.ret_code = LTTCOMM_SESSION_FAIL; + //lur.ret_code = LTTCOMM_SESSION_FAIL; + lur.ret_code = ret; + } + switch (lum->cmd) { + case LTTNG_UST_STREAM: + /* + * Special-case reply to send stream info. + * Use lum.u output. + */ + lur.u.stream.memory_map_size = lum->u.stream.memory_map_size; + shm_fd = lum->u.stream.shm_fd; + wait_fd = lum->u.stream.wait_fd; + break; + case LTTNG_UST_CHANNEL: + lur.u.channel.memory_map_size = lum->u.channel.memory_map_size; + shm_fd = lum->u.channel.shm_fd; + wait_fd = lum->u.channel.wait_fd; + break; } ret = send_reply(sock, &lur); + if (ret < 0) { + perror("error sending reply"); + goto error; + } + if ((lum->cmd == LTTNG_UST_STREAM || lum->cmd == LTTNG_UST_CHANNEL) + && lur.ret_code == LTTCOMM_OK) { + /* we also need to send the file descriptors. */ + ret = lttcomm_send_fds_unix_sock(sock, + &shm_fd, &shm_fd, + 1, sizeof(int)); + if (ret < 0) { + perror("send shm_fd"); + goto error; + } + ret = lttcomm_send_fds_unix_sock(sock, + &wait_fd, &wait_fd, + 1, sizeof(int)); + if (ret < 0) { + perror("send wait_fd"); + goto error; + } + } +error: ust_unlock(); return ret; } @@ -308,82 +358,163 @@ void cleanup_sock_info(struct sock_info *sock_info) } } +/* + * Using fork to set umask in the child process (not multi-thread safe). + * We deal with the shm_open vs ftruncate race (happening when the + * sessiond owns the shm and does not let everybody modify it, to ensure + * safety against shm_unlink) by simply letting the mmap fail and + * retrying after a few seconds. + * For global shm, everybody has rw access to it until the sessiond + * starts. + */ static -char *get_map_shm(struct sock_info *sock_info) +int get_wait_shm(struct sock_info *sock_info, size_t mmap_size) { - size_t mmap_size = sysconf(_SC_PAGE_SIZE); int wait_shm_fd, ret; - char *wait_shm_mmap; - int mode; - - mode = S_IRUSR | S_IRGRP; - if (sock_info->global) - mode |= S_IROTH; + pid_t pid; /* - * Get existing (read-only) shm, or open new shm. - * First try to open read-only. + * Try to open read-only. */ - wait_shm_fd = shm_open(sock_info->wait_shm_path, - O_RDONLY, mode); - if (wait_shm_fd >= 0) - goto got_shm; - /* - * Real-only open did not work. If it is because it did - * not exist, try creating it. Else it's a failure that - * prohibits using shm. - */ - if (errno != ENOENT) { - ERR("Error opening shm %s", sock_info->wait_shm_path); - goto error; - } - wait_shm_fd = shm_open(sock_info->wait_shm_path, - O_RDWR | O_CREAT | O_EXCL, mode | S_IWUSR); - if (wait_shm_fd >= 0) - goto created_shm; - if (errno != EEXIST) { + wait_shm_fd = shm_open(sock_info->wait_shm_path, O_RDONLY, 0); + if (wait_shm_fd >= 0) { + goto end; + } else if (wait_shm_fd < 0 && errno != ENOENT) { + /* + * Real-only open did not work, and it's not because the + * entry was not present. It's a failure that prohibits + * using shm. + */ ERR("Error opening shm %s", sock_info->wait_shm_path); - goto error; + goto end; } /* - * If another process beat us to create the shm, we are - * pretty certain the shm is available for us in - * read-only mode. + * If the open failed because the file did not exist, try + * creating it ourself. */ - wait_shm_fd = shm_open(sock_info->wait_shm_path, - O_RDWR | O_CREAT | O_EXCL, mode); - if (wait_shm_fd >= 0) - goto got_shm; - else - goto error; + pid = fork(); + if (pid > 0) { + int status; -created_shm: - ret = ftruncate(wait_shm_fd, mmap_size); - if (ret) { - PERROR("ftruncate"); - ret = close(wait_shm_fd); + /* + * Parent: wait for child to return, in which case the + * shared memory map will have been created. + */ + pid = wait(&status); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + wait_shm_fd = -1; + goto end; + } + /* + * Try to open read-only again after creation. + */ + wait_shm_fd = shm_open(sock_info->wait_shm_path, O_RDONLY, 0); + if (wait_shm_fd < 0) { + /* + * Real-only open did not work. It's a failure + * that prohibits using shm. + */ + ERR("Error opening shm %s", sock_info->wait_shm_path); + goto end; + } + goto end; + } else if (pid == 0) { + int create_mode; + + /* Child */ + create_mode = S_IRUSR | S_IWUSR | S_IRGRP; + if (sock_info->global) + create_mode |= S_IROTH | S_IWGRP | S_IWOTH; + /* + * We're alone in a child process, so we can modify the + * process-wide umask. + */ + umask(~create_mode); + /* + * Try creating shm (or get rw access). + * We don't do an exclusive open, because we allow other + * processes to create+ftruncate it concurrently. + */ + wait_shm_fd = shm_open(sock_info->wait_shm_path, + O_RDWR | O_CREAT, create_mode); + if (wait_shm_fd >= 0) { + ret = ftruncate(wait_shm_fd, mmap_size); + if (ret) { + PERROR("ftruncate"); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } + /* + * For local shm, we need to have rw access to accept + * opening it: this means the local sessiond will be + * able to wake us up. For global shm, we open it even + * if rw access is not granted, because the root.root + * sessiond will be able to override all rights and wake + * us up. + */ + if (!sock_info->global && errno != EACCES) { + ERR("Error opening shm %s", sock_info->wait_shm_path); + exit(EXIT_FAILURE); + } + /* + * The shm exists, but we cannot open it RW. Report + * success. + */ + exit(EXIT_SUCCESS); + } else { + return -1; + } +end: + if (wait_shm_fd >= 0 && !sock_info->global) { + struct stat statbuf; + + /* + * Ensure that our user is the owner of the shm file for + * local shm. If we do not own the file, it means our + * sessiond will not have access to wake us up (there is + * probably a rogue process trying to fake our + * sessiond). Fallback to polling method in this case. + */ + ret = fstat(wait_shm_fd, &statbuf); if (ret) { - ERR("Error closing fd"); + PERROR("fstat"); + goto error_close; } - wait_shm_fd = -1; - goto error; + if (statbuf.st_uid != getuid()) + goto error_close; } - /* Drop write access ASAP */ - ret = chmod(sock_info->wait_shm_path, mode); + return wait_shm_fd; + +error_close: + ret = close(wait_shm_fd); if (ret) { - PERROR("chmod"); + PERROR("Error closing fd"); } -got_shm: - wait_shm_mmap = mmap(NULL, mmap_size, PROT_READ, - MAP_SHARED, wait_shm_fd, 0); - if (wait_shm_mmap == MAP_FAILED) { - PERROR("mmap"); + return -1; +} + +static +char *get_map_shm(struct sock_info *sock_info) +{ + size_t mmap_size = sysconf(_SC_PAGE_SIZE); + int wait_shm_fd, ret; + char *wait_shm_mmap; + + wait_shm_fd = get_wait_shm(sock_info, mmap_size); + if (wait_shm_fd < 0) { goto error; } + wait_shm_mmap = mmap(NULL, mmap_size, PROT_READ, + MAP_SHARED, wait_shm_fd, 0); /* close shm fd immediately after taking the mmap reference */ ret = close(wait_shm_fd); if (ret) { - ERR("Error closing fd"); + PERROR("Error closing fd"); + } + if (wait_shm_mmap == MAP_FAILED) { + DBG("mmap error (can be caused by race with sessiond). Fallback to poll mode."); + goto error; } return wait_shm_mmap; @@ -394,10 +525,15 @@ error: static void wait_for_sessiond(struct sock_info *sock_info) { + int ret; + ust_lock(); if (lttng_ust_comm_should_quit) { goto quit; } + if (wait_poll_fallback) { + goto error; + } if (!sock_info->wait_shm_mmap) { sock_info->wait_shm_mmap = get_map_shm(sock_info); if (!sock_info->wait_shm_mmap) @@ -406,9 +542,23 @@ void wait_for_sessiond(struct sock_info *sock_info) ust_unlock(); DBG("Waiting for %s apps sessiond", sock_info->name); - /* Wait for futex wakeup TODO */ - sleep(5); - + /* Wait for futex wakeup */ + if (uatomic_read((int32_t *) sock_info->wait_shm_mmap) == 0) { + ret = futex_async((int32_t *) sock_info->wait_shm_mmap, + FUTEX_WAIT, 0, NULL, NULL, 0); + if (ret < 0) { + if (errno == EFAULT) { + wait_poll_fallback = 1; + ERR( +"Linux kernels 2.6.33 to 3.0 (with the exception of stable versions) " +"do not support FUTEX_WAKE on read-only memory mappings correctly. " +"Please upgrade your kernel " +"(fix is commit 9ea71503a8ed9184d2d0b8ccc4d269d05f7940ae in Linux kernel " +"mainline). LTTng-UST will use polling mode fallback."); + } + PERROR("futex"); + } + } return; quit: @@ -417,8 +567,6 @@ quit: error: ust_unlock(); - /* Error handling: fallback on a 5 seconds sleep. */ - sleep(5); return; } @@ -433,10 +581,25 @@ static void *ust_listener_thread(void *arg) { struct sock_info *sock_info = arg; - int sock, ret; + int sock, ret, prev_connect_failed = 0, has_waited = 0; /* Restart trying to connect to the session daemon */ restart: + if (prev_connect_failed) { + /* Wait for sessiond availability with pipe */ + wait_for_sessiond(sock_info); + if (has_waited) { + has_waited = 0; + /* + * Sleep for 5 seconds before retrying after a + * sequence of failure / wait / failure. This + * deals with a killed or broken session daemon. + */ + sleep(5); + } + has_waited = 1; + prev_connect_failed = 0; + } ust_lock(); if (lttng_ust_comm_should_quit) { @@ -456,6 +619,7 @@ restart: ret = lttcomm_connect_unix_sock(sock_info->sock_path); if (ret < 0) { ERR("Error connecting to %s apps socket", sock_info->name); + prev_connect_failed = 1; /* * If we cannot find the sessiond daemon, don't delay * constructor execution. @@ -463,9 +627,6 @@ restart: ret = handle_register_done(sock_info); assert(!ret); ust_unlock(); - - /* Wait for sessiond availability with pipe */ - wait_for_sessiond(sock_info); goto restart; } @@ -488,6 +649,7 @@ restart: ret = register_app_to_sessiond(sock); if (ret < 0) { ERR("Error registering to %s apps socket", sock_info->name); + prev_connect_failed = 1; /* * If we cannot register to the sessiond daemon, don't * delay constructor execution. @@ -495,7 +657,6 @@ restart: ret = handle_register_done(sock_info); assert(!ret); ust_unlock(); - wait_for_sessiond(sock_info); goto restart; } ust_unlock();