/** * Broadcast a message on a bus * * @param bus Bus information * @param message The message to write, may not be longer than * `BUS_MEMORY_SIZE` including the NUL-termination * @param flags `BUS_NOWAIT` if this function shall fail if * another process is currently running this * procedure * @return 0 on success, -1 on error */ int bus_write(const bus_t *bus, const char *message, int flags) { int saved_errno; #ifndef BUS_SEMAPHORES_ARE_SYNCHRONOUS int state = 0; #endif if (acquire_semaphore(bus, X, SEM_UNDO | F(BUS_NOWAIT, IPC_NOWAIT)) == -1) return -1; t(zero_semaphore(bus, W, 0)); write_shared_memory(bus, message); #ifndef BUS_SEMAPHORES_ARE_SYNCHRONOUS t(release_semaphore(bus, N, SEM_UNDO)); state++; #endif t(write_semaphore(bus, Q, 0)); t(zero_semaphore(bus, S, 0)); #ifndef BUS_SEMAPHORES_ARE_SYNCHRONOUS t(acquire_semaphore(bus, N, SEM_UNDO)); state--; #endif t(release_semaphore(bus, X, SEM_UNDO)); return 0; fail: saved_errno = errno; #ifndef BUS_SEMAPHORES_ARE_SYNCHRONOUS if (state > 0) acquire_semaphore(bus, N, SEM_UNDO); #endif release_semaphore(bus, X, SEM_UNDO); errno = saved_errno; return -1; }
/** * Wait for a message to be broadcasted on the bus. * The caller should make a copy of the received message, * without freeing the original copy, and parse it in a * separate thread. When the new thread has started be * started, the caller of this function should then * either call `bus_poll` again or `bus_poll_stop`. * * @param bus Bus information * @param flags `BUS_NOWAIT` if the bus should fail and set `errno` to * `EAGAIN` if there isn't already a message available on the bus * @return The received message, `NULL` on error */ const char * bus_poll(bus_t *bus, int flags) { int state = 0, saved_errno; if (!bus->first_poll) { t(release_semaphore(bus, W, SEM_UNDO)); state++; t(acquire_semaphore(bus, S, SEM_UNDO)); state++; t(zero_semaphore(bus, S, 0)); #ifndef BUS_SEMAPHORES_ARE_SYNCHRONOUS_ME_HARDER t(zero_semaphore(bus, N, 0)); #endif t(release_semaphore(bus, S, SEM_UNDO)); state--; t(acquire_semaphore(bus, W, SEM_UNDO)); state--; t(release_semaphore(bus, Q, 0)); } else { bus->first_poll = 0; } state--; t(zero_semaphore(bus, Q, F(BUS_NOWAIT, IPC_NOWAIT))); return bus->message; fail: saved_errno = errno; if (state > 1) release_semaphore(bus, S, SEM_UNDO); if (state > 0) acquire_semaphore(bus, W, SEM_UNDO); if (state < 0) bus->first_poll = 1; errno = saved_errno; return NULL; }
static int dpram_write_full(struct modemctl *mc, char __user *firmware, int size) { struct onedram_head_t onedram; u32 data_offset = ONEDRAM_DL_DATA_OFFSET; u32 head_offset = ONEDRAM_DL_HEADER_OFFSET; u32 checksum; pr_debug("%s\n", __func__); acquire_semaphore(mc); /* write full data of firmware */ dpram_write_from_user(mc, data_offset, firmware, size); /* check checksum */ checksum = onedram_checksum(0, (u8 *)(mc->mmio + data_offset), size); onedram.signature = ONEDRAM_DL_SIGNATURE; onedram.is_boot_update = 0; onedram.is_nv_update = 0; onedram.length = size; onedram.checksum = checksum; /* write header info */ memcpy(mc->mmio + head_offset, (u8 *)&onedram, sizeof(onedram)); return 0; }
/** * Listen (in a loop, forever) for new message on a bus * * @param bus Bus information * @param callback Function to call when a message is received, the * input parameters will be the read message and * `user_data` from `bus_read`'s parameter with the * same name. The message must have been parsed or * copied when `callback` returns as it may be over * overridden after that time. `callback` should * return either of the the values: * * 0: stop listening * * 1: continue listening * * -1: an error has occurred * However, the function [`bus_read`] will invoke * `callback` with `message` set to `NULL`one time * directly after it has started listening on the * bus. This is to the the program now it can safely * continue with any action that requires that the * programs is listening on the bus. * @param user_data Parameter passed to `callback` * @param timeout The time the operation shall fail with errno set * to `EAGAIN` if not completed, note that the callback * function may or may not have been called * @param clockid The ID of the clock the `timeout` is measured with, * it most be a predictable clock * @return 0 on success, -1 on error */ int bus_read_timed(const bus_t *bus, int (*callback)(const char *message, void *user_data), void *user_data, const struct timespec *timeout, clockid_t clockid) { int r, state = 0, saved_errno; struct timespec delta; if (!timeout) return bus_read(bus, callback, user_data); DELTA; if (release_semaphore_timed(bus, S, SEM_UNDO, &delta) == -1) return -1; t(r = callback(NULL, user_data)); if (!r) goto done; for (;;) { DELTA; t(release_semaphore_timed(bus, Q, 0, &delta)); DELTA; t(zero_semaphore_timed(bus, Q, 0, &delta)); t(r = callback(bus->message, user_data)); if (!r) goto done; t(release_semaphore(bus, W, SEM_UNDO)); state++; t(acquire_semaphore(bus, S, SEM_UNDO)); state++; t(zero_semaphore(bus, S, 0)); #ifndef BUS_SEMAPHORES_ARE_SYNCHRONOUS_ME_HARDER t(zero_semaphore(bus, N, 0)); #endif t(release_semaphore(bus, S, SEM_UNDO)); state--; t(acquire_semaphore(bus, W, SEM_UNDO)); state--; } fail: saved_errno = errno; if (state > 1) release_semaphore(bus, S, SEM_UNDO); if (state > 0) acquire_semaphore(bus, W, SEM_UNDO); acquire_semaphore(bus, S, SEM_UNDO); errno = saved_errno; return -1; done: t(acquire_semaphore(bus, S, SEM_UNDO)); return 0; }
void produce(int *memory, int semaphore_id) { while(1) { acquire_semaphore(semaphore_id); if(memory[SIZE_INDEX] < ARRAY_SIZE) { create_task(memory); } release_semaphore(semaphore_id); sleep(1); } }
static int dpram_write_delta(struct modemctl *mc, char __user *firmware, int size) { int ret = 0; pr_debug("%s\n", __func__); acquire_semaphore(mc); /* write the sizeof firmware */ write_single_data(mc, DPRAM_FIRMWARE_SIZE_ADDR, size); /* write the data of firmware */ dpram_write_from_user(mc, DPRAM_FIRMWARE_ADDR, firmware, size); return ret; }
/** * Broadcast a message on a bus * * @param bus Bus information * @param message The message to write, may not be longer than * `BUS_MEMORY_SIZE` including the NUL-termination * @param timeout The time the operation shall fail with errno set * to `EAGAIN` if not completed * @param clockid The ID of the clock the `timeout` is measured with, * it most be a predictable clock * @return 0 on success, -1 on error */ int bus_write_timed(const bus_t *bus, const char *message, const struct timespec *timeout, clockid_t clockid) { int saved_errno; #ifndef BUS_SEMAPHORES_ARE_SYNCHRONOUS int state = 0; #endif struct timespec delta; if (!timeout) return bus_write(bus, message, 0); DELTA; if (acquire_semaphore_timed(bus, X, SEM_UNDO, &delta) == -1) return -1; DELTA; t(zero_semaphore_timed(bus, W, 0, &delta)); write_shared_memory(bus, message); #ifndef BUS_SEMAPHORES_ARE_SYNCHRONOUS t(release_semaphore(bus, N, SEM_UNDO)); state++; #endif t(write_semaphore(bus, Q, 0)); t(zero_semaphore(bus, S, 0)); #ifndef BUS_SEMAPHORES_ARE_SYNCHRONOUS t(acquire_semaphore(bus, N, SEM_UNDO)); state--; #endif t(release_semaphore(bus, X, SEM_UNDO)); return 0; fail: saved_errno = errno; #ifndef BUS_SEMAPHORES_ARE_SYNCHRONOUS if (state > 0) acquire_semaphore(bus, N, SEM_UNDO); #endif release_semaphore(bus, X, SEM_UNDO); errno = saved_errno; return -1; }
static int dpram_update_delta(struct modemctl *mc) { int err = 0; int msec = 0; u32 val; pr_debug("%s\n", __func__); acquire_semaphore(mc); /* write boot magic */ write_single_data(mc, DPRAM_BOOT_MAGIC_ADDR, DPRAM_BOOT_MAGIC_RECOVERY_FOTA); write_single_data(mc, DPRAM_BOOT_TYPE_ADDR, DPRAM_BOOT_TYPE_DPRAM_DELTA); /* At this point modem is powered off. So power on modem */ err = dpram_modem_pwron(mc); if (err < 0) { pr_err("modem_reset() fail : %d", modem_pwr_status(mc)); return err; } /* clear mailboxBA */ set_mailbox_ba(mc, 0xFFFFFFFF); /* wait for job sync message */ while (true) { val = get_mailbox_ab(mc); if ((val & STATUS_JOB_MAGIC_M) == STATUS_JOB_MAGIC_CODE) { err = 0; break; } msleep(1); if (++msec > 20000) { err = -2; pr_err("Failed to sync with modem (%x)", val); return err; } if ((msec % 1000) == 0) pr_info("Waiting for sync message... 0x%08x (pwr:%s)", \ val, modem_pwr_status(mc) ? "ON" : "OFF"); } if (err == 0) { pr_info("Modem ready to start the firmware update"); /* let modem start the job */ set_mailbox_ba(mc, STATUS_JOB_MAGIC_CODE); /* If we have the semaphore, toss it to modem. */ return_semaphore(mc); } return err; }
/** * Listen (in a loop, forever) for new message on a bus * * @param bus Bus information * @param callback Function to call when a message is received, the * input parameters will be the read message and * `user_data` from `bus_read`'s parameter with the * same name. The message must have been parsed or * copied when `callback` returns as it may be over * overridden after that time. `callback` should * return either of the the values: * * 0: stop listening * * 1: continue listening * * -1: an error has occurred * However, the function [`bus_read`] will invoke * `callback` with `message` set to `NULL`one time * directly after it has started listening on the * bus. This is to the the program now it can safely * continue with any action that requires that the * programs is listening on the bus. * @param user_data Parameter passed to `callback` * @return 0 on success, -1 on error */ int bus_read(const bus_t *bus, int (*callback)(const char *message, void *user_data), void *user_data) { int r, state = 0, saved_errno; if (release_semaphore(bus, S, SEM_UNDO) == -1) return -1; t(r = callback(NULL, user_data)); if (!r) goto done; for (;;) { t(release_semaphore(bus, Q, 0)); t(zero_semaphore(bus, Q, 0)); t(r = callback(bus->message, user_data)); if (!r) goto done; t(release_semaphore(bus, W, SEM_UNDO)); state++; t(acquire_semaphore(bus, S, SEM_UNDO)); state++; t(zero_semaphore(bus, S, 0)); #ifndef BUS_SEMAPHORES_ARE_SYNCHRONOUS_ME_HARDER t(zero_semaphore(bus, N, 0)); #endif t(release_semaphore(bus, S, SEM_UNDO)); state--; t(acquire_semaphore(bus, W, SEM_UNDO)); state--; } fail: saved_errno = errno; if (state > 1) release_semaphore(bus, S, SEM_UNDO); if (state > 0) acquire_semaphore(bus, W, SEM_UNDO); acquire_semaphore(bus, S, SEM_UNDO); errno = saved_errno; return -1; done: t(acquire_semaphore(bus, S, SEM_UNDO)); return 0; }
/** * Wait for a message to be broadcasted on the bus. * The caller should make a copy of the received message, * without freeing the original copy, and parse it in a * separate thread. When the new thread has started be * started, the caller of this function should then * either call `bus_poll_timed` again or `bus_poll_stop`. * * @param bus Bus information * @param timeout The time the operation shall fail with errno set * to `EAGAIN` if not completed * @param clockid The ID of the clock the `timeout` is measured with, * it most be a predictable clock * @return The received message, `NULL` on error */ const char *bus_poll_timed(bus_t *bus, const struct timespec *timeout, clockid_t clockid) { int state = 0, saved_errno; struct timespec delta; if (!timeout) return bus_poll(bus, 0); if (!bus->first_poll) { t(release_semaphore(bus, W, SEM_UNDO)); state++; t(acquire_semaphore(bus, S, SEM_UNDO)); state++; t(zero_semaphore(bus, S, 0)); #ifndef BUS_SEMAPHORES_ARE_SYNCHRONOUS_ME_HARDER t(zero_semaphore(bus, N, 0)); #endif t(release_semaphore(bus, S, SEM_UNDO)); state--; t(acquire_semaphore(bus, W, SEM_UNDO)); state--; t(release_semaphore(bus, Q, 0)); } else { bus->first_poll = 0; } state--; DELTA; t(zero_semaphore_timed(bus, Q, 0, &delta)); return bus->message; fail: saved_errno = errno; if (state > 1) release_semaphore(bus, S, SEM_UNDO); if (state > 0) acquire_semaphore(bus, W, SEM_UNDO); if (state < 0) bus->first_poll = 1; errno = saved_errno; return NULL; }
void output_dump (struct output *out) { int outfd_not_empty = FD_NOT_EMPTY (out->out); int errfd_not_empty = FD_NOT_EMPTY (out->err); if (outfd_not_empty || errfd_not_empty) { int traced = 0; /* Try to acquire the semaphore. If it fails, dump the output unsynchronized; still better than silently discarding it. We want to keep this lock for as little time as possible. */ void *sem = acquire_semaphore (); /* Log the working directory for this dump. */ if (print_directory_flag && output_sync != OUTPUT_SYNC_RECURSE) traced = log_working_directory (1); if (outfd_not_empty) pump_from_tmp (out->out, stdout); if (errfd_not_empty && out->err != out->out) pump_from_tmp (out->err, stderr); if (traced) log_working_directory (0); /* Exit the critical section. */ if (sem) release_semaphore (sem); /* Truncate and reset the output, in case we use it again. */ if (out->out != OUTPUT_NONE) { int e; lseek (out->out, 0, SEEK_SET); EINTRLOOP (e, ftruncate (out->out, 0)); } if (out->err != OUTPUT_NONE && out->err != out->out) { int e; lseek (out->err, 0, SEEK_SET); EINTRLOOP (e, ftruncate (out->err, 0)); } } }
static int dpram_chk_full_update(struct modemctl *mc, int __user *pct, char __user *msg) { int err; u32 status = 0; u32 phone_active = 0; bool is_reboot = 0; err = 3; mc->dpram_prev_status = 0xFFFFFFFF; mc->dpram_prev_phone_active = 0xFFFFFFFF; retry: phone_active = modem_pwr_status(mc); status = get_mailbox_ab(mc); pr_debug("PHONE %d Mailbox 0x%x\n", phone_active, status); if ((mc->dpram_prev_phone_active != phone_active) || (mc->dpram_prev_status != status)) { mc->dpram_prev_phone_active = phone_active; mc->dpram_prev_status = status; } if (!phone_active) { if (status == ONEDRAM_DL_COMPLETE) { pr_info("*OK* ONEDRAM_DL_COMPLETE\n"); err = 0; } else if (status == ONEDRAM_DL_DONE_AND_RESET) { pr_info("*OK* ONEDRAM_DONE_AND_RESET\n"); dpram_modem_pwron(mc); goto retry; } } if (status == ONEDRAM_DL_CHECKSUM_ERR) { pr_info("*ERROR* ONEDRAM_DL_CHECKSUM_ERR\n"); is_reboot = 1; } else if (status == ONEDRAM_DL_ERASE_WRITE_ERR) { pr_info("*ERROR* ONEDRAM_DL_ERASE_WRITE_ERR\n"); is_reboot = 1; } else if (status == ONEDRAM_DL_BOOT_UPDATE_ERR) { pr_info("*ERROR* ONEDRAM_DL_BOOT_UPDATE_ERR\n"); is_reboot = 1; } else if (status == ONEDRAM_DL_REWRITE_FAIL_ERR) { pr_info("*ERROR* ONEDRAM_DL_REWRITE_FAIL_ERR\n"); is_reboot = 1; } else if (status == ONEDRAM_DL_LENGTH_CH_FAIL) { pr_info("*ERROR* ONEDRAM_DL_LENGTH_CH_FAIL\n"); is_reboot = 1; } else { if (status != mc->dpram_prev_status) pr_info("*ERROR* %d, 0x%x\n", phone_active, status); } if (is_reboot) { pr_info("system reboot necessary\n"); err = -1; } if (err <= 0) { pr_info("Update done.\n"); acquire_semaphore(mc); write_single_data(mc, 0, 0xffffffff); } return err; }
/** * Announce that the thread has stopped listening on the bus. * This is required so that the thread does not cause others * to wait indefinitely. * * @param bus Bus information * @return 0 on success, -1 on error */ int bus_poll_stop(const bus_t *bus) { return acquire_semaphore(bus, S, SEM_UNDO | IPC_NOWAIT); }
int main() { int rc; char s[1024]; char last_message_i_wrote[256]; char md5ified_message[256]; int i = 0; int done = 0; struct param_struct params; int shm_id; void *address = NULL; int sem_id; struct shmid_ds shm_info; say(MY_NAME, "Oooo 'ello, I'm Mrs. Premise!"); read_params(¶ms); // Create the shared memory shm_id = shmget(params.key, params.size, IPC_CREAT | IPC_EXCL | params.permissions); if (shm_id == -1) { shm_id = 0; sprintf(s, "Creating the shared memory failed; errno is %d", errno); say(MY_NAME, s); } else { sprintf(s, "Shared memory's id is %d", shm_id); say(MY_NAME, s); // Attach the memory. address = shmat(shm_id, NULL, 0); if ((void *)-1 == address) { address = NULL; sprintf(s, "Attaching the shared memory failed; errno is %d", errno); say(MY_NAME, s); } else { sprintf(s, "shared memory address = %p", address); say(MY_NAME, s); } } if (address) { // Create the semaphore sem_id = semget(params.key, 1, IPC_CREAT | IPC_EXCL | params.permissions); if (-1 == sem_id) { sem_id = 0; sprintf(s, "Creating the semaphore failed; errno is %d", errno); say(MY_NAME, s); } else { sprintf(s, "the semaphore id is %d", sem_id); say(MY_NAME, s); // I seed the shared memory with a random string (the current time). get_current_time(s); strcpy((char *)address, s); strcpy(last_message_i_wrote, s); sprintf(s, "Wrote %zu characters: %s", strlen(last_message_i_wrote), last_message_i_wrote); say(MY_NAME, s); i = 0; while (!done) { sprintf(s, "iteration %d", i); say(MY_NAME, s); // Release the semaphore... rc = release_semaphore(MY_NAME, sem_id, params.live_dangerously); // ...and wait for it to become available again. In real code // I might want to sleep briefly before calling .acquire() in // order to politely give other processes an opportunity to grab // the semaphore while it is free so as to avoid starvation. But // this code is meant to be a stress test that maximizes the // opportunity for shared memory corruption and politeness is // not helpful in stress tests. if (!rc) rc = acquire_semaphore(MY_NAME, sem_id, params.live_dangerously); if (rc) done = 1; else { // I keep checking the shared memory until something new has // been written. while ( (!rc) && \ (!strcmp((char *)address, last_message_i_wrote)) ) { // Nothing new; give Mrs. Conclusion another change to respond. sprintf(s, "Read %zu characters '%s'", strlen((char *)address), (char *)address); say(MY_NAME, s); rc = release_semaphore(MY_NAME, sem_id, params.live_dangerously); if (!rc) { rc = acquire_semaphore(MY_NAME, sem_id, params.live_dangerously); } } if (rc) done = 1; else { sprintf(s, "Read %zu characters '%s'", strlen((char *)address), (char *)address); say(MY_NAME, s); // What I read must be the md5 of what I wrote or something's // gone wrong. md5ify(last_message_i_wrote, md5ified_message); if (strcmp(md5ified_message, (char *)address) == 0) { // Yes, the message is OK i++; if (i == params.iterations) done = 1; // MD5 the reply and write back to Mrs. Conclusion. md5ify(md5ified_message, md5ified_message); sprintf(s, "Writing %zu characters '%s'", strlen(md5ified_message), md5ified_message); say(MY_NAME, s); strcpy((char *)address, md5ified_message); strcpy((char *)last_message_i_wrote, md5ified_message); } else { sprintf(s, "Shared memory corruption after %d iterations.", i); say(MY_NAME, s); sprintf(s, "Mismatch; new message is '%s', expected '%s'.", (char *)address, md5ified_message); say(MY_NAME, s); done = 1; } } } } // Announce for one last time that the semaphore is free again so that // Mrs. Conclusion can exit. say(MY_NAME, "Final release of the semaphore followed by a 5 second pause"); rc = release_semaphore(MY_NAME, sem_id, params.live_dangerously); sleep(5); // ...before beginning to wait until it is free again. // Technically, this is bad practice. It's possible that on a // heavily loaded machine, Mrs. Conclusion wouldn't get a chance // to acquire the semaphore. There really ought to be a loop here // that waits for some sort of goodbye message but for purposes of // simplicity I'm skipping that. say(MY_NAME, "Final wait to acquire the semaphore"); rc = acquire_semaphore(MY_NAME, sem_id, params.live_dangerously); if (!rc) { say(MY_NAME, "Destroying the shared memory."); if (-1 == shmdt(address)) { sprintf(s, "Detaching the memory failed; errno is %d", errno); say(MY_NAME, s); } address = NULL; if (-1 == shmctl(shm_id, IPC_RMID, &shm_info)) { sprintf(s, "Removing the memory failed; errno is %d", errno); say(MY_NAME, s); } } } say(MY_NAME, "Destroying the semaphore."); // Clean up the semaphore if (-1 == semctl(sem_id, 0, IPC_RMID)) { sprintf(s, "Removing the semaphore failed; errno is %d", errno); say(MY_NAME, s); } } return 0; }