mirror of https://github.com/trapexit/mergerfs.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							240 lines
						
					
					
						
							4.7 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							240 lines
						
					
					
						
							4.7 KiB
						
					
					
				
								/*
							 | 
						|
								  ISC License
							 | 
						|
								
							 | 
						|
								  Copyright (c) 2022, Antonio SJ Musumeci <trapexit@spawn.link>
							 | 
						|
								  Permission to use, copy, modify, and/or distribute this software for any
							 | 
						|
								  purpose with or without fee is hereby granted, provided that the above
							 | 
						|
								  copyright notice and this permission notice appear in all copies.
							 | 
						|
								
							 | 
						|
								  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
							 | 
						|
								  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
							 | 
						|
								  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
							 | 
						|
								  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
							 | 
						|
								  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
							 | 
						|
								  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
							 | 
						|
								  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
							 | 
						|
								*/
							 | 
						|
								
							 | 
						|
								#include "fuse_msgbuf.hpp"
							 | 
						|
								
							 | 
						|
								#include "fuse.h"
							 | 
						|
								#include "fuse_kernel.h"
							 | 
						|
								
							 | 
						|
								#include <unistd.h>
							 | 
						|
								
							 | 
						|
								#include <atomic>
							 | 
						|
								#include <cstdint>
							 | 
						|
								#include <cstdlib>
							 | 
						|
								#include <mutex>
							 | 
						|
								#include <vector>
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								static std::uint32_t g_PAGESIZE = 0;
							 | 
						|
								static std::uint32_t g_BUFSIZE  = 0;
							 | 
						|
								
							 | 
						|
								static std::atomic<std::uint_fast64_t> g_MSGBUF_ALLOC_COUNT;
							 | 
						|
								
							 | 
						|
								static std::mutex g_MUTEX;
							 | 
						|
								static std::vector<fuse_msgbuf_t*> g_MSGBUF_STACK;
							 | 
						|
								
							 | 
						|
								uint64_t
							 | 
						|
								msgbuf_get_bufsize()
							 | 
						|
								{
							 | 
						|
								  return g_BUFSIZE;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								u32
							 | 
						|
								msgbuf_get_pagesize()
							 | 
						|
								{
							 | 
						|
								  return g_PAGESIZE;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// +1 page so that it is used for the "header" of the allocation as
							 | 
						|
								// well as to allow for offseting for write requests to be page
							 | 
						|
								// aligned. +1 again for fuse header as the max_pages value is for the
							 | 
						|
								// body.
							 | 
						|
								void
							 | 
						|
								msgbuf_set_bufsize(const uint32_t size_in_pages_)
							 | 
						|
								{
							 | 
						|
								  g_BUFSIZE = ((size_in_pages_ + 2) * g_PAGESIZE);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static
							 | 
						|
								void
							 | 
						|
								_msgbuf_page_align(fuse_msgbuf_t *msgbuf_)
							 | 
						|
								{
							 | 
						|
								  msgbuf_->mem   = (char*)msgbuf_;
							 | 
						|
								  msgbuf_->mem  += g_PAGESIZE;
							 | 
						|
								  msgbuf_->size  = (g_BUFSIZE - g_PAGESIZE);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static
							 | 
						|
								void
							 | 
						|
								_msgbuf_write_align(fuse_msgbuf_t *msgbuf_)
							 | 
						|
								{
							 | 
						|
								  msgbuf_->mem   = (char*)msgbuf_;
							 | 
						|
								  msgbuf_->mem  += g_PAGESIZE;
							 | 
						|
								  msgbuf_->mem  -= sizeof(struct fuse_in_header);
							 | 
						|
								  msgbuf_->mem  -= sizeof(struct fuse_write_in);
							 | 
						|
								  msgbuf_->size  = (g_BUFSIZE - g_PAGESIZE);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static
							 | 
						|
								__attribute__((constructor))
							 | 
						|
								void
							 | 
						|
								_msgbuf_constructor()
							 | 
						|
								{
							 | 
						|
								  g_PAGESIZE = sysconf(_SC_PAGESIZE);
							 | 
						|
								
							 | 
						|
								  msgbuf_set_bufsize(FUSE_DEFAULT_MAX_MAX_PAGES);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static
							 | 
						|
								__attribute__((destructor))
							 | 
						|
								void
							 | 
						|
								_msgbuf_destructor()
							 | 
						|
								{
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static
							 | 
						|
								void*
							 | 
						|
								_page_aligned_malloc(const uint64_t size_)
							 | 
						|
								{
							 | 
						|
								  int rv;
							 | 
						|
								  void *buf = NULL;
							 | 
						|
								
							 | 
						|
								  rv = posix_memalign(&buf,g_PAGESIZE,size_);
							 | 
						|
								  if(rv != 0)
							 | 
						|
								    return NULL;
							 | 
						|
								
							 | 
						|
								  return buf;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								typedef void (*msgbuf_setup_func_t)(fuse_msgbuf_t*);
							 | 
						|
								
							 | 
						|
								static
							 | 
						|
								fuse_msgbuf_t*
							 | 
						|
								_msgbuf_alloc(msgbuf_setup_func_t setup_func_)
							 | 
						|
								{
							 | 
						|
								  fuse_msgbuf_t *msgbuf;
							 | 
						|
								
							 | 
						|
								  g_MUTEX.lock();
							 | 
						|
								  if(g_MSGBUF_STACK.empty())
							 | 
						|
								    {
							 | 
						|
								      g_MUTEX.unlock();
							 | 
						|
								
							 | 
						|
								      msgbuf = (fuse_msgbuf_t*)_page_aligned_malloc(g_BUFSIZE);
							 | 
						|
								      if(msgbuf == NULL)
							 | 
						|
								        return NULL;
							 | 
						|
								
							 | 
						|
								      g_MSGBUF_ALLOC_COUNT.fetch_add(1,std::memory_order_relaxed);
							 | 
						|
								    }
							 | 
						|
								  else
							 | 
						|
								    {
							 | 
						|
								      msgbuf = g_MSGBUF_STACK.back();
							 | 
						|
								      g_MSGBUF_STACK.pop_back();
							 | 
						|
								      g_MUTEX.unlock();
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								  setup_func_(msgbuf);
							 | 
						|
								
							 | 
						|
								  return msgbuf;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Offset the memory so write request payload will be placed on page
							 | 
						|
								// boundry so O_DIRECT can work. No impact on other message types
							 | 
						|
								// except for `read` which will require using `msgbuf_page_align`.
							 | 
						|
								fuse_msgbuf_t*
							 | 
						|
								msgbuf_alloc()
							 | 
						|
								{
							 | 
						|
								  return _msgbuf_alloc(_msgbuf_write_align);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								fuse_msgbuf_t*
							 | 
						|
								msgbuf_alloc_page_aligned()
							 | 
						|
								{
							 | 
						|
								  return _msgbuf_alloc(_msgbuf_page_align);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static
							 | 
						|
								void
							 | 
						|
								msgbuf_destroy(fuse_msgbuf_t *msgbuf_)
							 | 
						|
								{
							 | 
						|
								  free(msgbuf_);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void
							 | 
						|
								msgbuf_free(fuse_msgbuf_t *msgbuf_)
							 | 
						|
								{
							 | 
						|
								  std::lock_guard<std::mutex> lck(g_MUTEX);
							 | 
						|
								
							 | 
						|
								  if(msgbuf_->size != (g_BUFSIZE - g_PAGESIZE))
							 | 
						|
								    {
							 | 
						|
								      msgbuf_destroy(msgbuf_);
							 | 
						|
								      g_MSGBUF_ALLOC_COUNT.fetch_sub(1,std::memory_order_relaxed);
							 | 
						|
								      return;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								  g_MSGBUF_STACK.emplace_back(msgbuf_);
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								uint64_t
							 | 
						|
								msgbuf_alloc_count()
							 | 
						|
								{
							 | 
						|
								  return g_MSGBUF_ALLOC_COUNT;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								uint64_t
							 | 
						|
								msgbuf_avail_count()
							 | 
						|
								{
							 | 
						|
								  std::lock_guard<std::mutex> lck(g_MUTEX);
							 | 
						|
								
							 | 
						|
								  return g_MSGBUF_STACK.size();
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void
							 | 
						|
								msgbuf_gc_10percent()
							 | 
						|
								{
							 | 
						|
								  std::vector<fuse_msgbuf_t*> togc;
							 | 
						|
								
							 | 
						|
								  {
							 | 
						|
								    std::size_t size;
							 | 
						|
								    std::size_t ten_percent;
							 | 
						|
								
							 | 
						|
								    std::lock_guard<std::mutex> lck(g_MUTEX);
							 | 
						|
								
							 | 
						|
								    size        = g_MSGBUF_STACK.size();
							 | 
						|
								    ten_percent = (size / 10);
							 | 
						|
								
							 | 
						|
								    for(std::size_t i = 0; i < ten_percent; i++)
							 | 
						|
								      {
							 | 
						|
								        togc.push_back(g_MSGBUF_STACK.back());
							 | 
						|
								        g_MSGBUF_STACK.pop_back();
							 | 
						|
								      }
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  for(auto msgbuf : togc)
							 | 
						|
								    {
							 | 
						|
								      msgbuf_destroy(msgbuf);
							 | 
						|
								      g_MSGBUF_ALLOC_COUNT.fetch_sub(1,std::memory_order_relaxed);
							 | 
						|
								    }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								void
							 | 
						|
								msgbuf_gc()
							 | 
						|
								{
							 | 
						|
								  std::vector<fuse_msgbuf_t*> oldstack;
							 | 
						|
								
							 | 
						|
								  {
							 | 
						|
								    std::lock_guard<std::mutex> lck(g_MUTEX);
							 | 
						|
								    oldstack.swap(g_MSGBUF_STACK);
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  for(auto msgbuf: oldstack)
							 | 
						|
								    {
							 | 
						|
								      msgbuf_destroy(msgbuf);
							 | 
						|
								      g_MSGBUF_ALLOC_COUNT.fetch_sub(1,std::memory_order_relaxed);
							 | 
						|
								    }
							 | 
						|
								}
							 |