Add in RAM storage to dataman

This commit is contained in:
David Sidrane 2016-09-21 13:21:08 -10:00 committed by Lorenz Meier
parent 0544a6ee32
commit dce2262243

View File

@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
* Copyright (c) 2013-2016 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -38,12 +38,14 @@
* @author Lorenz Meier
* @author Julian Oes
* @author Thomas Gubler
* @author David Sidrane
*/
#include <px4_config.h>
#include <px4_defines.h>
#include <px4_posix.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <fcntl.h>
#include <systemlib/systemlib.h>
@ -52,6 +54,7 @@
#include <string.h>
#include <semaphore.h>
#include <unistd.h>
#include <platforms/px4_getopt.h>
#include "dataman.h"
#include <systemlib/param/param.h>
@ -71,6 +74,43 @@ __EXPORT void dm_lock(dm_item_t item);
__EXPORT void dm_unlock(dm_item_t item);
__EXPORT int dm_restart(dm_reset_reason restart_type);
/* File based Operations */
static ssize_t _file_write(dm_item_t item, unsigned char index, dm_persitence_t persistence, const void *buf,
size_t count);
static ssize_t _file_read(dm_item_t item, unsigned char index, void *buf, size_t count);
static int _file_clear(dm_item_t item);
static int _file_restart(dm_reset_reason reason);
/* Ram based Operations */
static ssize_t _ram_write(dm_item_t item, unsigned char index, dm_persitence_t persistence, const void *buf,
size_t count);
static ssize_t _ram_read(dm_item_t item, unsigned char index, void *buf, size_t count);
static int _ram_clear(dm_item_t item);
static int _ram_restart(dm_reset_reason reason);
typedef struct dm_operations_t {
ssize_t (*write)(dm_item_t item, unsigned char index, dm_persitence_t persistence, const void *buf, size_t count);
ssize_t (*read)(dm_item_t item, unsigned char index, void *buf, size_t count);
int (*clear)(dm_item_t item);
int (*restart)(dm_reset_reason reason);
} dm_operations_t;
static dm_operations_t dm_file_operations = {
.write = _file_write,
.read = _file_read,
.clear = _file_clear,
.restart = _file_restart,
};
static dm_operations_t dm_ram_operations = {
.write = _ram_write,
.read = _ram_read,
.clear = _ram_clear,
.restart = _ram_restart,
};
static dm_operations_t *g_dm_ops = &dm_file_operations;
/** Types of function calls supported by the worker task */
typedef enum {
dm_write_func = 0,
@ -133,13 +173,18 @@ static px4_sem_t *g_item_locks[DM_KEY_NUM_KEYS];
static px4_sem_t g_sys_state_mutex;
/* The data manager store file handle and file name */
static int g_fd = -1, g_task_fd = -1;
static int g_fd = -1;
static int g_task_fd = -1;
#ifdef __PX4_POSIX_EAGLE
static const char *default_device_path = PX4_ROOTFSDIR"/dataman";
#else
static const char *default_device_path = PX4_ROOTFSDIR"/fs/microsd/dataman";
#endif
static char *k_data_manager_device_path = NULL;
static uint8_t *g_data = NULL;
static uint8_t *g_task_data = NULL;
static uint8_t *g_task_data_end = NULL;
static bool g_on_disk = true;
/* The data manager work queues */
@ -294,6 +339,10 @@ enqueue_work_item_and_wait_for_result(work_q_item_t *item)
return result;
}
static bool is_running(void)
{
return (g_on_disk ? g_fd != -1 : g_data != NULL);
}
/* Calculate the offset in file of specific item */
static int
calculate_offset(dm_item_t item, unsigned char index)
@ -324,9 +373,47 @@ calculate_offset(dm_item_t item, unsigned char index)
* The total size must not exceed k_sector_size
*/
/* write to the data manager RAM buffer */
static ssize_t _ram_write(dm_item_t item, unsigned char index, dm_persitence_t persistence, const void *buf,
size_t count)
{
/* Get the offset for this item */
int offset = calculate_offset(item, index);
/* If item type or index out of range, return error */
if (offset < 0) {
return -1;
}
/* Make sure caller has not given us more data than we can handle */
if (count > DM_MAX_DATA_SIZE) {
return -1;
}
uint8_t *buffer = &g_task_data[offset];
if (buffer > g_task_data_end) {
return -1;
}
/* Write out the data, prefixed with length and persistence level */
buffer[0] = count;
buffer[1] = persistence;
buffer[2] = 0;
buffer[3] = 0;
if (count > 0) {
memcpy(buffer + DM_SECTOR_HDR_SIZE, buf, count);
}
/* All is well... return the number of user data written */
return count;
}
/* write to the data manager file */
static ssize_t
_write(dm_item_t item, unsigned char index, dm_persitence_t persistence, const void *buf, size_t count)
_file_write(dm_item_t item, unsigned char index, dm_persitence_t persistence, const void *buf, size_t count)
{
unsigned char buffer[k_sector_size];
size_t len;
@ -374,9 +461,48 @@ _write(dm_item_t item, unsigned char index, dm_persitence_t persistence, const v
return count - DM_SECTOR_HDR_SIZE;
}
/* Retrieve from the data manager RAM buffer*/
static ssize_t _ram_read(dm_item_t item, unsigned char index, void *buf, size_t count)
{
/* Get the offset for this item */
int offset = calculate_offset(item, index);
/* If item type or index out of range, return error */
if (offset < 0) {
return -1;
}
/* Make sure the caller hasn't asked for more data than we can handle */
if (count > DM_MAX_DATA_SIZE) {
return -1;
}
/* Read the prefix and data */
uint8_t *buffer = &g_task_data[offset];
if (buffer > g_task_data_end) {
return -1;
}
/* See if we got data */
if (buffer[0] > 0) {
/* We got more than requested!!! */
if (buffer[0] > count) {
return -1;
}
/* Looks good, copy it to the caller's buffer */
memcpy(buf, buffer + DM_SECTOR_HDR_SIZE, buffer[0]);
}
/* Return the number of bytes of caller data read */
return buffer[0];
}
/* Retrieve from the data manager file */
static ssize_t
_read(dm_item_t item, unsigned char index, void *buf, size_t count)
_file_read(dm_item_t item, unsigned char index, void *buf, size_t count)
{
unsigned char buffer[k_sector_size];
int len, offset;
@ -426,8 +552,37 @@ _read(dm_item_t item, unsigned char index, void *buf, size_t count)
return buffer[0];
}
static int _ram_clear(dm_item_t item)
{
int i;
int result = 0;
/* Get the offset of 1st item of this type */
int offset = calculate_offset(item, 0);
/* Check for item type out of range */
if (offset < 0) {
return -1;
}
/* Clear all items of this type */
for (i = 0; (unsigned)i < g_per_item_max_index[item]; i++) {
uint8_t *buf = &g_task_data[offset];
if (buf > g_task_data_end) {
result = -1;
break;
}
buf[0] = 0;
offset += k_sector_size;
}
return result;
}
static int
_clear(dm_item_t item)
_file_clear(dm_item_t item)
{
int i, result = 0;
@ -476,9 +631,56 @@ _clear(dm_item_t item)
return result;
}
/** Tell the data manager about the type of the last reset */
static int _ram_restart(dm_reset_reason reason)
{
int offset = 0;
int result = 0;
/* We need to scan the entire file and invalidate and data that should not persist after the last reset */
/* Loop through all of the data segments and delete those that are not persistent */
while (1) {
/* Get data segment at current offset */
uint8_t *buffer = &g_task_data[offset];
if (buffer >= g_task_data_end) {
break;
}
/* check if segment contains data */
if (buffer[0]) {
int clear_entry = 0;
/* Whether data gets deleted depends on reset type and data segment's persistence setting */
if (reason == DM_INIT_REASON_POWER_ON) {
if (buffer[1] > DM_PERSIST_POWER_ON_RESET) {
clear_entry = 1;
}
} else {
if (buffer[1] > DM_PERSIST_IN_FLIGHT_RESET) {
clear_entry = 1;
}
}
/* Set segment to unused if data does not persist */
if (clear_entry) {
buffer[0] = 0;
}
}
offset += k_sector_size;
}
/* tell the caller how it went */
return result;
}
static int
_restart(dm_reset_reason reason)
_file_restart(dm_reset_reason reason)
{
unsigned char buffer[2];
int offset = 0, result = 0;
@ -552,7 +754,7 @@ dm_write(dm_item_t item, unsigned char index, dm_persitence_t persistence, const
work_q_item_t *work;
/* Make sure data manager has been started and is not shutting down */
if ((g_fd < 0) || g_task_should_exit) {
if (!is_running() || g_task_should_exit) {
return -1;
}
@ -579,7 +781,7 @@ dm_read(dm_item_t item, unsigned char index, void *buf, size_t count)
work_q_item_t *work;
/* Make sure data manager has been started and is not shutting down */
if ((g_fd < 0) || g_task_should_exit) {
if (!is_running() || g_task_should_exit) {
return -1;
}
@ -604,7 +806,7 @@ dm_clear(dm_item_t item)
work_q_item_t *work;
/* Make sure data manager has been started and is not shutting down */
if ((g_fd < 0) || g_task_should_exit) {
if (!is_running() || g_task_should_exit) {
return -1;
}
@ -624,7 +826,7 @@ __EXPORT void
dm_lock(dm_item_t item)
{
/* Make sure data manager has been started and is not shutting down */
if ((g_fd < 0) || g_task_should_exit) {
if (!is_running() || g_task_should_exit) {
return;
}
@ -641,7 +843,7 @@ __EXPORT void
dm_unlock(dm_item_t item)
{
/* Make sure data manager has been started and is not shutting down */
if ((g_fd < 0) || g_task_should_exit) {
if (!is_running() || g_task_should_exit) {
return;
}
@ -661,7 +863,7 @@ dm_restart(dm_reset_reason reason)
work_q_item_t *work;
/* Make sure data manager has been started and is not shutting down */
if ((g_fd < 0) || g_task_should_exit) {
if (!is_running() || g_task_should_exit) {
return -1;
}
@ -680,6 +882,10 @@ dm_restart(dm_reset_reason reason)
static int
task_main(int argc, char *argv[])
{
/* Dataman can use disk or RAM */
bool on_disk = k_data_manager_device_path != NULL;
work_q_item_t *work;
/* Initialize global variables */
@ -711,48 +917,66 @@ task_main(int argc, char *argv[])
px4_sem_init(&g_work_queued_sema, 1, 0);
/* See if the data manage file exists and is a multiple of the sector size */
g_task_fd = open(k_data_manager_device_path, O_RDONLY | O_BINARY);
if (!on_disk) {
if (g_task_fd >= 0) {
/* In memory */
g_task_data = malloc(max_offset);
#ifndef __PX4_POSIX
// XXX on Mac OS and Linux the file is not a multiple of the sector sizes
// this might need further inspection.
/* File exists, check its size */
int file_size = lseek(g_task_fd, 0, SEEK_END);
if ((file_size % k_sector_size) != 0) {
PX4_WARN("Incompatible data manager file %s, resetting it", k_data_manager_device_path);
PX4_WARN("Size: %u, sector size: %d", file_size, k_sector_size);
close(g_task_fd);
unlink(k_data_manager_device_path);
if (g_task_data == NULL) {
PX4_WARN("Could not allocate %d bytes of memory", max_offset);
px4_sem_post(&g_init_sema); /* Don't want to hang startup */
return -1;
}
memset(g_task_data, 0, max_offset);
g_task_data_end = &g_task_data[max_offset - 1];
} else {
/* See if the data manage file exists and is a multiple of the sector size */
g_task_fd = open(k_data_manager_device_path, O_RDONLY | O_BINARY);
if (g_task_fd >= 0) {
#ifndef __PX4_POSIX
// XXX on Mac OS and Linux the file is not a multiple of the sector sizes
// this might need further inspection.
/* File exists, check its size */
int file_size = lseek(g_task_fd, 0, SEEK_END);
if ((file_size % k_sector_size) != 0) {
PX4_WARN("Incompatible data manager file %s, resetting it", k_data_manager_device_path);
PX4_WARN("Size: %u, sector size: %d", file_size, k_sector_size);
close(g_task_fd);
unlink(k_data_manager_device_path);
}
#else
close(g_task_fd);
close(g_task_fd);
#endif
}
/* Open or create the data manager file */
g_task_fd = open(k_data_manager_device_path, O_RDWR | O_CREAT | O_BINARY, PX4_O_MODE_666);
if (g_task_fd < 0) {
PX4_WARN("Could not open data manager file %s", k_data_manager_device_path);
px4_sem_post(&g_init_sema); /* Don't want to hang startup */
return -1;
}
if ((unsigned)lseek(g_task_fd, max_offset, SEEK_SET) != max_offset) {
close(g_task_fd);
PX4_WARN("Could not seek data manager file %s", k_data_manager_device_path);
px4_sem_post(&g_init_sema); /* Don't want to hang startup */
return -1;
}
fsync(g_task_fd);
}
/* Open or create the data manager file */
g_task_fd = open(k_data_manager_device_path, O_RDWR | O_CREAT | O_BINARY, PX4_O_MODE_666);
if (g_task_fd < 0) {
PX4_WARN("Could not open data manager file %s", k_data_manager_device_path);
px4_sem_post(&g_init_sema); /* Don't want to hang startup */
return -1;
}
if ((unsigned)lseek(g_task_fd, max_offset, SEEK_SET) != max_offset) {
close(g_task_fd);
PX4_WARN("Could not seek data manager file %s", k_data_manager_device_path);
px4_sem_post(&g_init_sema); /* Don't want to hang startup */
return -1;
}
fsync(g_task_fd);
g_dm_ops = on_disk ? &dm_file_operations : &dm_ram_operations;
/* see if we need to erase any items based on restart type */
int sys_restart_val;
@ -762,21 +986,33 @@ task_main(int argc, char *argv[])
if (param_get(param_find("SYS_RESTART_TYPE"), &sys_restart_val) == OK) {
if (sys_restart_val == DM_INIT_REASON_POWER_ON) {
restart_type_str = "Power on restart";
_restart(DM_INIT_REASON_POWER_ON);
g_dm_ops->restart(DM_INIT_REASON_POWER_ON);
} else if (sys_restart_val == DM_INIT_REASON_IN_FLIGHT) {
restart_type_str = "In flight restart";
_restart(DM_INIT_REASON_IN_FLIGHT);
g_dm_ops->restart(DM_INIT_REASON_IN_FLIGHT);
}
}
/* We use two file descriptors, one for the caller context and one for the worker thread */
/* We use two file descriptors or memory pointers, one for the caller context and one for the worker thread */
/* They are actually the same but we need to some way to reject caller request while the */
/* worker thread is shutting down but still processing requests */
g_fd = g_task_fd;
PX4_INFO("%s, data manager file '%s' size is %d bytes",
restart_type_str, k_data_manager_device_path, max_offset);
g_fd = g_task_fd;
g_data = g_task_data;
/* g_on_disk defaults to true
* Now qualify is_running based on storages
*/
g_on_disk = on_disk;
if (g_on_disk) {
PX4_INFO("%s, data manager file '%s' size is %d bytes",
restart_type_str, k_data_manager_device_path, max_offset);
} else {
PX4_INFO("%s, data manager RAM size is %d bytes",
restart_type_str, max_offset);
}
/* Tell startup that the worker thread has completed its initialization */
px4_sem_post(&g_init_sema);
@ -785,14 +1021,23 @@ task_main(int argc, char *argv[])
while (true) {
/* do we need to exit ??? */
if ((g_task_should_exit) && (g_fd >= 0)) {
/* Close the file handle to stop further queuing */
g_fd = -1;
}
if (!g_task_should_exit) {
/* wait for work */
px4_sem_wait(&g_work_queued_sema);
} else {
if (g_on_disk) {
/* Mark the file handle closed the to stop further queuing */
if (g_fd >= 0) {
g_fd = -1;
}
} else {
/* Mark the memory freed the to stop further queuing */
if (g_data) {
g_data = NULL;
}
}
}
/* Empty the work queue */
@ -803,24 +1048,25 @@ task_main(int argc, char *argv[])
case dm_write_func:
g_func_counts[dm_write_func]++;
work->result =
_write(work->write_params.item, work->write_params.index, work->write_params.persistence, work->write_params.buf,
work->write_params.count);
g_dm_ops->write(work->write_params.item, work->write_params.index, work->write_params.persistence,
work->write_params.buf,
work->write_params.count);
break;
case dm_read_func:
g_func_counts[dm_read_func]++;
work->result =
_read(work->read_params.item, work->read_params.index, work->read_params.buf, work->read_params.count);
g_dm_ops->read(work->read_params.item, work->read_params.index, work->read_params.buf, work->read_params.count);
break;
case dm_clear_func:
g_func_counts[dm_clear_func]++;
work->result = _clear(work->clear_params.item);
work->result = g_dm_ops->clear(work->clear_params.item);
break;
case dm_restart_func:
g_func_counts[dm_restart_func]++;
work->result = _restart(work->restart_params.reason);
work->result = g_dm_ops->restart(work->restart_params.reason);
break;
default: /* should never happen */
@ -833,13 +1079,23 @@ task_main(int argc, char *argv[])
}
/* time to go???? */
if ((g_task_should_exit) && (g_fd < 0)) {
if ((g_task_should_exit) && !is_running()) {
break;
}
}
close(g_task_fd);
if (on_disk) {
close(g_task_fd);
} else {
free(g_task_data);
}
g_task_data = NULL;
g_task_data_end = NULL;
g_task_fd = -1;
/* revert back to qualifying is_running based on disk */
g_on_disk = true;
/* The work queue is now empty, empty the free queue */
for (;;) {
@ -902,12 +1158,14 @@ stop(void)
static void
usage(void)
{
PX4_INFO("usage: dataman {start [-f datafile]|stop|status|poweronrestart|inflightrestart}");
PX4_INFO("usage: dataman {start [-f datafile]|[-r]|stop|status|poweronrestart|inflightrestart}");
}
int
dataman_main(int argc, char *argv[])
{
bool in_ram = false;
if (argc < 2) {
usage();
return -1;
@ -915,22 +1173,49 @@ dataman_main(int argc, char *argv[])
if (!strcmp(argv[1], "start")) {
if (g_fd >= 0) {
if (is_running()) {
PX4_WARN("dataman already running");
return -1;
}
if (argc == 4 && strcmp(argv[2], "-f") == 0) {
k_data_manager_device_path = strdup(argv[3]);
PX4_INFO("dataman file set to: %s", k_data_manager_device_path);
int ch;
int dmoptind = 1;
const char *dmoptarg = NULL;
} else {
/* jump over start and look at options first */
while ((ch = px4_getopt(argc, argv, "f:r", &dmoptind, &dmoptarg)) != EOF) {
switch (ch) {
case 'f':
k_data_manager_device_path = strdup(dmoptarg);
PX4_INFO("dataman file set to: %s", k_data_manager_device_path);
break;
case 'r':
in_ram = true;
break;
//no break
default:
usage();
return -1;
}
}
if (k_data_manager_device_path != NULL && in_ram) {
PX4_WARN("-f and -r are mutually exclusive");
usage();
return -1;
}
if (k_data_manager_device_path == NULL && !in_ram) {
k_data_manager_device_path = strdup(default_device_path);
}
start();
if (g_fd < 0) {
if (!is_running()) {
PX4_ERR("dataman start failed");
free(k_data_manager_device_path);
k_data_manager_device_path = NULL;
@ -941,7 +1226,7 @@ dataman_main(int argc, char *argv[])
}
/* Worker thread should be running for all other commands */
if (g_fd < 0) {
if (!is_running()) {
PX4_WARN("dataman worker thread not running");
usage();
return -1;