Ejemplo n.º 1
0
int rwb_write(FAR struct rwbuffer_s *rwb, off_t startblock, size_t nblocks, FAR const uint8_t *wrbuffer)
{
	int ret = OK;

#ifdef CONFIG_DRVR_READAHEAD
	if (rwb->rhmaxblocks > 0) {
		/* If the new write data overlaps any part of the read buffer, then
		 * flush the data from the read buffer.  We could attempt some more
		 * exotic handling -- but this simple logic is well-suited for simple
		 * streaming applications.
		 */

		rwb_semtake(&rwb->rhsem);
		if (rwb_overlap(rwb->rhblockstart, rwb->rhnblocks, startblock, nblocks)) {
			rwb_resetrhbuffer(rwb);
		}

		rwb_semgive(&rwb->rhsem);
	}
#endif

#ifdef CONFIG_DRVR_WRITEBUFFER
	if (rwb->wrmaxblocks > 0) {
		fvdbg("startblock=%d wrbuffer=%p\n", startblock, wrbuffer);

		/* Use the block cache unless the buffer size is bigger than block cache */

		if (nblocks > rwb->wrmaxblocks) {
			/* First flush the cache */

			rwb_semtake(&rwb->wrsem);
			rwb_wrflush(rwb);
			rwb_semgive(&rwb->wrsem);

			/* Then transfer the data directly to the media */

			ret = rwb->wrflush(rwb->dev, wrbuffer, startblock, nblocks);
		} else {
			/* Buffer the data in the write buffer */

			ret = rwb_writebuffer(rwb, startblock, nblocks, wrbuffer);
		}

		/* On success, return the number of blocks that we were requested to
		 * write.  This is for compatibility with the normal return of a block
		 * driver write method
		 */
	} else
#endif
	{
		/* No write buffer.. just pass the write operation through via the
		 * flush callback.
		 */

		ret = rwb->wrflush(rwb->dev, wrbuffer, startblock, nblocks);
	}

	return ret;
}
Ejemplo n.º 2
0
int rwb_mediaremoved(FAR struct rwbuffer_s *rwb)
{
#ifdef CONFIG_FS_WRITEBUFFER
    rwb_semtake(&rwb->wrsem);
    rwb_resetwrbuffer(rwb);
    rwb_semgive(&rwb->wrsem);
#endif

#ifdef CONFIG_FS_READAHEAD
    rwb_semtake(&rwb->rhsem);
    rwb_resetrhbuffer(rwb);
    rwb_semgive(&rwb->rhsem);
#endif
    return 0;
}
Ejemplo n.º 3
0
static void rwb_wrflush(struct rwbuffer_s *rwb)
{
  int ret;

  fvdbg("Timeout!\n");

  rwb_semtake(&rwb->wrsem);
  if (rwb->wrnblocks > 0)
    {
      fvdbg("Flushing: blockstart=0x%08lx nblocks=%d from buffer=%p\n",
      (long)rwb->wrblockstart, rwb->wrnblocks, rwb->wrbuffer);

      /* Flush cache.  On success, the flush method will return the number
       * of blocks written.  Anything other than the number requested is
       * an error.
       */

      ret = rwb->wrflush(rwb->dev, rwb->wrbuffer, rwb->wrblockstart, rwb->wrnblocks);
      if (ret != rwb->wrnblocks)
        {
          fdbg("ERROR: Error flushing write buffer: %d\n", ret);
        }

      rwb_resetwrbuffer(rwb);
    }

  rwb_semgive(&rwb->wrsem);
}
Ejemplo n.º 4
0
int rwb_mediaremoved(FAR struct rwbuffer_s *rwb)
{
#ifdef CONFIG_DRVR_WRITEBUFFER
	if (rwb->wrmaxblocks > 0) {
		rwb_semtake(&rwb->wrsem);
		rwb_resetwrbuffer(rwb);
		rwb_semgive(&rwb->wrsem);
	}
#endif

#ifdef CONFIG_DRVR_READAHEAD
	if (rwb->rhmaxblocks > 0) {
		rwb_semtake(&rwb->rhsem);
		rwb_resetrhbuffer(rwb);
		rwb_semgive(&rwb->rhsem);
	}
#endif

	return OK;
}
Ejemplo n.º 5
0
static void rwb_wrtimeout(FAR void *arg)
{
  /* The following assumes that the size of a pointer is 4-bytes or less */

  FAR struct rwbuffer_s *rwb = (struct rwbuffer_s *)arg;
  DEBUGASSERT(rwb != NULL);

  /* If a timeout elapses with with write buffer activity, this watchdog
   * handler function will be evoked on the thread of execution of the
   * worker thread.
   */

  rwb_semtake(&rwb->wrsem);
  rwb_wrflush(rwb);
  rwb_semgive(&rwb->wrsem);
}
Ejemplo n.º 6
0
int rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock, uint32_t nblocks,
             FAR uint8_t *rdbuffer)
{
  uint32_t remaining;

  fvdbg("startblock=%ld nblocks=%ld rdbuffer=%p\n",
        (long)startblock, (long)nblocks, rdbuffer);

#ifdef CONFIG_DRVR_WRITEBUFFER
  /* If the new read data overlaps any part of the write buffer, then
   * flush the write data onto the physical media before reading.  We
   * could attempt some more exotic handling -- but this simple logic
   * is well-suited for simple streaming applications.
   */

  if (rwb->wrmaxblocks > 0)
    {
      /* If the write buffer overlaps the block(s) requested, then flush the
       * write buffer.
       */

      rwb_semtake(&rwb->wrsem);
      if (rwb_overlap(rwb->wrblockstart, rwb->wrnblocks, startblock, nblocks))
        {
          rwb_wrflush(rwb);
        }

      rwb_semgive(&rwb->wrsem);
    }
#endif

#ifdef CONFIG_DRVR_READAHEAD
  if (rhmaxblocks > 0)
    {
      /* Loop until we have read all of the requested blocks */

      rwb_semtake(&rwb->rhsem);
      for (remaining = nblocks; remaining > 0;)
        {
          /* Is there anything in the read-ahead buffer? */

          if (rwb->rhnblocks > 0)
            {
              off_t  startblock = startblock;
              size_t nbufblocks = 0;
              off_t  bufferend;

              /* Loop for each block we find in the read-head buffer.  Count
               * the number of buffers that we can read from read-ahead
               * buffer.
               */

              bufferend = rwb->rhblockstart + rwb->rhnblocks;

              while ((startblock >= rwb->rhblockstart) &&
                     (startblock < bufferend) &&
                     (remaining > 0))
                {
                  /* This is one more that we will read from the read ahead
                   * buffer.
                   */

                  nbufblocks++;

                  /* And one less that we will read from the media */

                  startblock++;
                  remaining--;
                }

              /* Then read the data from the read-ahead buffer */

             rwb_bufferread(rwb, startblock, nbufblocks, &rdbuffer);
            }

          /* If we did not get all of the data from the buffer, then we have
           * to refill the buffer and try again.
           */

          if (remaining > 0)
            {
              int ret = rwb_rhreload(rwb, startblock);
              if (ret < 0)
                {
                  fdbg("ERROR: Failed to fill the read-ahead buffer: %d\n", ret);
                  return ret;
                }
            }
        }

      /* On success, return the number of blocks that we were requested to
       * read. This is for compatibility with the normal return of a block
       * driver read method
       */

      rwb_semgive(&rwb->rhsem);
      ret = nblocks;
    }
  else
#else
    {
      /* No read-ahead buffering, (re)load the data directly into
       * the user buffer.
       */

      ret = rwb->rhreload(rwb->dev, startblock, nblocks, rdbuffer);
    }
#endif

  return ret;
}
Ejemplo n.º 7
0
int rwb_invalidate_readahead(FAR struct rwbuffer_s *rwb,
                               off_t startblock, size_t blockcount)
{
  int ret;

  if (rwb->rhmaxblocks > 0 && rhnblocks > 0)
    {
      off_t rhbend;
      off_t invend;

      fvdbg("startblock=%d blockcount=%p\n", startblock, blockcount);

      rwb_semtake(&rwb->rhsem);

      /* Now there are five cases:
       *
       * 1. We invalidate nothing
       */

      rhbend = rwb->rhblockstart + rwb->rhnblocks;
      invend = startblock + blockcount;

      if (rwb->rhblockstart > invend || rhbend < startblock)
        {
          ret = OK;
        }

      /* 2. We invalidate the entire read-ahead buffer. */

      else if (rwb->rhblockstart >= startblock && rhbend <= invend)
        {
          rwb->rhnblocks = 0;
          ret = OK;
        }

      /* We are going to invalidate a subset of the read-ahead buffer.
       * Three more cases to consider:
       *
       * 2. We invalidate a portion in the middle of the write buffer
       */

      else if (rwb->rhblockstart < startblock && rhbend > invend)
        {
          /* Keep the blocks at the beginning of the buffer up the
           * start of the invalidated region.
           */

          rwb->rhnblocks = startblock - rwb->rhblockstart;
          ret = OK;
        }

      /* 3. We invalidate a portion at the end of the read-ahead buffer */

      else if (rhbend > startblock && rhbend <= invend)
        {
          rwb->rhnblocks = rhbend - startblock;
          ret = OK;
        }

      /* 4. We invalidate a portion at the beginning of the write buffer */

      else /* if (rwb->rhblockstart >= startblock && rhbend < invend) */
        {
          /* Let's just force the whole read-ahead buffer to be reloaded.
           * That might cost s small amount of performance, but well worth
           * the lower complexity.
           */

          DEBUGASSERT(rwb->rhblockstart >= startblock && rhbend < invend);
          rwb->rhnblocks = 0;
          ret = OK;
        }

      rwb_semgive(&rwb->rhsem);
    }

  return ret;
}
Ejemplo n.º 8
0
int rwb_invalidate_writebuffer(FAR struct rwbuffer_s *rwb,
                               off_t startblock, size_t blockcount)
{
  int ret;

  if (rwb->wrmaxblocks > 0 && wrnblocks > 0)
    {
      off_t wrbend;
      off_t invend;

      fvdbg("startblock=%d blockcount=%p\n", startblock, blockcount);

      rwb_semtake(&rwb->wrsem);

      /* Now there are five cases:
       *
       * 1. We invalidate nothing
       */

      wrbend = rwb->wrblockstart + rwb->wrnblocks;
      invend = startblock + blockcount;

      if (rwb->wrblockstart > invend || wrbend < startblock)
        {
          ret = OK;
        }

      /* 2. We invalidate the entire write buffer. */

      else if (rwb->wrblockstart >= startblock && wrbend <= invend)
        {
          rwb->wrnblocks = 0;
          ret = OK;
        }

      /* We are going to invalidate a subset of the write buffer.  Three
       * more cases to consider:
       *
       * 2. We invalidate a portion in the middle of the write buffer
       */

      else if (rwb->wrblockstart < startblock && wrbend > invend)
        {
          uint8_t *src;
          off_t    block;
          off_t    offset;
          size_t   nblocks;

          /* Write the blocks at the end of the media to hardware */

          nblocks = wrbend - invend;
          block   = invend;
          offset  = block - rwb->wrblockstart;
          src     = rwb->wrbuffer + offset * rwb->blocksize;

          ret = rwb->wrflush(rwb->dev, block, nblocks, src);
          if (ret < 0)
            {
              fdbg("ERROR: wrflush failed: %d\n", ret);
            }

          /* Keep the blocks at the beginning of the buffer up the
           * start of the invalidated region.
           */
          else
            {
              rwb->wrnblocks = startblock - rwb->wrblockstart;
              ret = OK;
            }
        }

      /* 3. We invalidate a portion at the end of the write buffer */

      else if (wrbend > startblock && wrbend <= invend)
        {
          rwb->wrnblocks = wrbend - startblock;
          ret = OK;
        }

      /* 4. We invalidate a portion at the beginning of the write buffer */

      else /* if (rwb->wrblockstart >= startblock && wrbend < invend) */
        {
          uint8_t *src;
          size_t   ninval;
          size_t   nkeep;

          DEBUGASSERT(rwb->wrblockstart >= startblock && wrbend < invend);

          /* Copy the data from the uninvalidated region to the beginning
           * of the write buffer.
           *
           * First calculate the source and destination of the transfer.
           */

          ninval = invend - rwb->wrblockstart;
          src    = rwb->wrbuffer + ninval * rwb->blocksize;

          /* Calculate the number of blocks we are keeping.  We keep
           * the ones that we don't invalidate.
           */

          nkeep  = rwb->wrnblocks - ninval;

          /* Then move the data that we are keeping to the beginning
           * the write buffer.
           */

          memcpy(rwb->wrbuffer, src, nkeep * rwb->blocksize);

          /* Update the block info.  The first block is now the one just
           * after the invalidation region and the number buffered blocks
           * is the number that we kept.
           */

          rwb->wrblockstart = invend;
          rwb->wrnblocks    = nkeep;
          ret = OK;
        }

      rwb_semgive(&rwb->wrsem);
    }

  return ret;
}