/* $Id: mcl_vrmem.cpp,v 1.1.1.1 2003/09/03 12:45:44 chneuman Exp $ */ /* * Copyright (c) 1999-2003 INRIA - Universite Paris 6 - All rights reserved * (main author: Vincent Roca - vincent.roca@inrialpes.fr) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ /* * This file implements a "virtual memory service" where received data * (ie. data and FEC DUs received) is stored on disk. * Shared by all mcl contexts. * Data in physical memory is a "read-only" version of data on file. * * LIMITATIONS: * - single very large file, located by default in /tmp. don't forget to * change default location if not enough room there. * - there is no possibility to remove a part of the file (eg. once an ADU * has been completely received and returned to the application). * The only possibility is to remove the whole file! */ #include "mcl_includes.h" #ifdef VIRTUAL_RX_MEM /* * private functions */ static void mcl_vrm_initialize (mclcb_t *mclcb); static int mcl_find_position_on_disk (mclcb_t *mclcb, du_t *du); static int mcl_copy_from_disk (mclcb_t *mclcb, du_t *du, int from_fd, int from_off, char *to, int len); /****** Public Functions ******************************************************/ /* * Can we use the vrm service to actually store data? * This is only possible if vrm service is used and there's no room * any more in the prm area. * returns a boolean: 1 if positive, 0 otherwise */ int mcl_vrm_can_store_in_vrm (mclcb_t *mclcb, int len) /* buffer length */ { vrm_cb_t *vcb; /* vrm control block ptr */ ASSERT(mclcb); if (!mclcb->vrm_used) { TRACELVL(5, (mcl_stdout, "<- mcl_vrm_can_store_in_vrm: vrm not used\n")) return 0; } if (!mclcb->vrm_initialized) mcl_vrm_initialize(mclcb); vcb = &(mclcb->vrm_cb); if (vcb->prm_size + len <= vcb->max_prm_size) { TRACELVL(5, (mcl_stdout, "<- mcl_vrm_can_store_in_vrm: no, room in prm\n")) /*TRACE(("\nin prm (sz=%d, tot_sz=%d)\n", vcb->prm_size, vcb->max_prm_size))*/ return 0; } else { TRACELVL(5, (mcl_stdout, "<- mcl_vrm_can_store_in_vrm: yes\n")) /*TRACE(("\ndisk (prm_sz=%d)\n", vcb->prm_size))*/ return 1; } } /* * Stores data of the DU in the vrm service. * Data is stored in arrival order in order to maximize the write-to-disk * speed. * Data is assumed to be in the buffer pointed by *data, not in du->data. * returns 0 if OK, < 0 if error */ int mcl_vrm_store_data (mclcb_t *mclcb, du_t *du, /* */ char *data, /* buffer */ int len) /* buffer length */ { u_int off; /* offset */ int l; vrm_cb_t *vcb = &(mclcb->vrm_cb); /* vrm control block ptr */ ASSERT(mclcb); ASSERT(mclcb->vrm_used); TRACELVL(5, (mcl_stdout, "-> mcl_vrm_store_data:\n")) if (!mclcb->vrm_initialized) mcl_vrm_initialize(mclcb); ASSERT(du); off = du->vrm_info.off = vcb->f_len; du->vrm_info.in_vrm = 1; vcb->f_len += len; du->data = NULL; /* not in phy mem */ /* repositionning within file is absolutely required! */ if (lseek(vcb->f_fd, off, SEEK_SET) < 0) { perror("mcl_vrm_store_data: ERROR: lseek failed\n"); goto bad; } /* store data */ if ((l = write(vcb->f_fd, data, len)) < 0) { perror("mcl_vrm_store_data: ERROR: read failed\n"); goto bad; } TRACELVL(5, (mcl_stdout, "<- mcl_vrm_store_data: starts at off=%d\n", off)) return 0; bad: TRACELVL(5, (mcl_stdout, "<- mcl_vrm_store_data: ERROR\n")) mcl_exit(-1); return -1; /* just to avoid a compiler warning! */ } /* * register this du in physical memory */ void mcl_vrm_register_in_prm (mclcb_t *mclcb, du_t *du, /* NULL if not applicable */ int len) /* buffer length */ { ASSERT(mclcb); ASSERT(du); ASSERT(len > 0); TRACELVL(5, (mcl_stdout, " mcl_vrm_register_in_prm:\n")) mclcb->vrm_cb.prm_size += len; ASSERT(mclcb->vrm_cb.prm_size <= mclcb->vrm_cb.max_prm_size); du->vrm_info.in_vrm = 0; /* false, it is in prm */ /*TRACE(("... reg in prm len=%d, new sz=%d\n", len, mclcb->vrm_cb.prm_size))*/ } /* * remove this du from physical memory */ void mcl_vrm_remove_from_prm (mclcb_t *mclcb, du_t *du, /* NULL if not applicable */ int len) /* buffer length */ { ASSERT(mclcb); ASSERT(du); ASSERT(len > 0); ASSERT(!du->vrm_info.in_vrm); TRACELVL(5, (mcl_stdout, " mcl_vrm_remove_from_prm:\n")) mclcb->vrm_cb.prm_size -= len; ASSERT(mclcb->vrm_cb.prm_size >= 0); /*TRACE(("... removed from prm len=%d, remains %d\n", len, mclcb->vrm_cb.prm_size))*/ } #ifdef DEBUG /* * is this du in vrm? */ int mcl_vrm_in_vrm (mclcb_t *mclcb, du_t *du) { ASSERT(mclcb); ASSERT(du); TRACELVL(5, (mcl_stdout, " mcl_vrm_in_vrm:\n")) return(du->vrm_info.in_vrm); } #else /* implemented as a macro for improved performances */ #endif /* DEBUG */ /* * Given a DU pointer, makes sure that the du->data pointer is a valid * pointer, retrieving data from disk if necessary. */ char * mcl_vrm_get_data (mclcb_t *mclcb, du_t *du, char *data, /* buffer */ int len) /* buffer length */ { int from_off; /* what offset on disk? */ vrm_cb_t *vcb = &(mclcb->vrm_cb); /* vrm control block ptr */ ASSERT(mclcb); TRACELVL(5, (mcl_stdout, "-> mcl_vrm_get_data:\n")) #if 0 if (du->data) { /* data buffer is present */ ASSERT(!du->vrm_info.in_vrm); TRACELVL(5, (mcl_stdout, "<- mcl_vrm_get_data: cached, ok\n")) return du->data; } #endif ASSERT(mclcb->vrm_used); ASSERT(mclcb->vrm_initialized); ASSERT(du->data == NULL); ASSERT(du->vrm_info.in_vrm); ASSERT((int)du->len == len); if ((from_off = mcl_find_position_on_disk(mclcb, du)) < 0) { PRINT_ERR((mcl_stderr, "mcl_vrm_get_data: ERROR, mcl_find_position_on_disk failed\n")) goto bad; } if (mcl_copy_from_disk(mclcb, du, vcb->f_fd, from_off, data, len)) { PRINT_ERR((mcl_stderr, "mcl_vrm_get_data: ERROR, mcl_copy_from_disk failed\n")) goto bad; } TRACELVL(5, (mcl_stdout, "<- mcl_vrm_get_data: from disk, ok\n")) return data; bad: TRACELVL(5, (mcl_stdout, "<- mcl_vrm_get_data: ERROR\n")) mcl_exit(-1); return NULL; /* just to avoid a compiler warning! */ } /* * Frees the vrm file and closes the service. * returns 0 if ok, an error code otherwise */ int mcl_vrm_close (mclcb_t *mclcb) { vrm_cb_t *vcb = &(mclcb->vrm_cb); /* vrm control block ptr */ if (!mclcb->vrm_initialized || !mclcb->vrm_used) return 0; TRACELVL(5, (mcl_stdout, "-> mcl_vrm_close:\n")) /* XXX: add a user counter and actually close when last one leaves!!! */ mclcb->vrm_initialized = 0; unlink(vcb->f_name); /* remove VTMEM file */ TRACELVL(5, (mcl_stdout, "<- mcl_vrm_close: ok\n")) return 0; } /****** Private Functions *****************************************************/ /* * initialize everything */ static void mcl_vrm_initialize (mclcb_t *mclcb) { vrm_cb_t *vcb = &(mclcb->vrm_cb);/* vrm control block */ TRACELVL(5, (mcl_stdout, "-> mcl_vrm_initialize:\n")) ASSERT(!mclcb->vrm_initialized); memset(vcb, 0, sizeof(vrm_cb_t)); vcb->max_prm_size = VIRTUAL_RX_MAX_PHY_MEM_SIZE; #if 0 vcb->entry_size = mclcb->datagram_sz; /* fixed size! */ if (!(pm_tab = (vrm_cache_entry_t*)calloc(mcl_vrm_cache_entry_nb, sizeof(vrm_cache_entry_t))) || !(pm_blk = (char*)malloc(mcl_vrm_cache_entry_nb * vcb->entry_size))) { PRINT_ERR((mcl_stderr,"mcl_vrmem_initialize: ERROR: out of memory")) mcl_exit(-1); } for (i = 0, pm = pm_tab, off = 0; i < (int)mcl_vrm_cache_entry_nb; i++, pm++, off += vcb->entry_size) { pm->data = pm_blk + off; } vcb->entry_nb = mcl_vrm_cache_entry_nb; vcb->pmem_tab = pm_tab; vcb->next_index = 0; #endif /* create temp file now in the tmp dir specified in mcl_tmp_dir_name */ #ifdef WIN32 strncpy(vcb->f_name, tempnam(mcl_tmp_dir_name, "mcl_vrm_"), MAX_FILE_NAME_LEN); if ((vcb->f_fd = open(vcb->f_name, _O_RDWR | O_CREAT | O_BINARY)) < 0) { perror("mcl_vrmem_initialize: ERROR"); mcl_exit(-1); } #else strncat(vcb->f_name, mcl_tmp_dir_name, MAX_FILE_NAME_LEN); strncat(vcb->f_name, "mcl_vrm_XXXXXX", MAX_FILE_NAME_LEN); if ((vcb->f_fd = mkstemp(vcb->f_name)) < 0) { perror("mcl_vrmem_initialize: ERROR"); mcl_exit(-1); } #endif mclcb->vrm_initialized = 1; TRACELVL(5, (mcl_stdout, "<- mcl_vrm_initialize:\n")) } /* * returns the offset within the vrm file, <0 if error */ static int mcl_find_position_on_disk (mclcb_t *mclcb, du_t *du) { u_int off; /* total offset within vrm file */ ASSERT(mclcb->vrm_initialized); off = du->vrm_info.off; TRACELVL(5, (mcl_stdout, " mcl_find_position_on_disk: off=%d\n", off)) return off; } static int mcl_copy_from_disk (mclcb_t *mclcb, du_t *du, int from_fd, int from_off, char *to, int len) { /*vrm_cb_t *vcb = &(mclcb->vrm_cb);*/ /* vrm control block ptr */ TRACELVL(5, (mcl_stdout, "-> mcl_copy_from_disk:\n")) ASSERT(mclcb->vrm_initialized); /* repositionning within file is absolutely required! */ if (lseek(from_fd, from_off, SEEK_SET) < 0) { perror("mcl_copy_from_disk: ERROR: lseek failed\n"); mcl_exit(-1); } if ((len = read(from_fd, to, len)) <= 0) { PRINT_ERR((mcl_stderr,"mcl_copy_from_disk: read returned %d", len)) perror("mcl_copy_from_disk: ERROR: read failed\n"); PRINT_ERR((mcl_stderr, "ses_id=%d, fd=%d, data=x%x, size=%d\n", mclcb->id, from_fd, (int)to, len)) mcl_exit(-1); } TRACELVL(5, (mcl_stdout, "<- mcl_copy_from_disk: %d copied\n", len)) return 0; } #endif /* VIRTUAL_RX_MEM */