Files
scylladb/core/xen/gntalloc.cc
Avi Kivity 38443e2c4c posix: change file_desc mmap API to return an mmap_area
An mmap_area munmap()s itself when destroyed, reclaiming memory.
2014-12-11 12:25:31 +02:00

224 lines
5.8 KiB
C++

#include <stdint.h>
#include <unistd.h>
#include <xen/xen.h>
#include <xen/grant_table.h> // Kernel interface
#include <xen/sys/gntalloc.h> // Userspace interface
#include <atomic>
#include "osv_xen.hh"
#include "gntalloc.hh"
namespace xen {
gntref invalid_ref;
// FIXME: Most of the destructors are yet to be coded
//
class userspace_grant_head : public grant_head {
std::atomic<int> _ref_head = { 0 };
std::vector<gntref> _refs;
public:
userspace_grant_head(std::vector<gntref> v) : _refs(v) {}
virtual gntref new_ref() override;
virtual gntref new_ref(void *addr, size_t size) override;
virtual void free_ref(gntref& ref);
};
class userspace_gntalloc : public gntalloc {
file_desc _gntalloc;
mmap_area _gntmap;
struct ioctl_gntalloc_alloc_gref *get_gref(unsigned nr_ents);
public:
explicit userspace_gntalloc(unsigned otherend);
~userspace_gntalloc();
virtual gntref alloc_ref() override;
virtual grant_head *alloc_ref(unsigned refs) override;
};
userspace_gntalloc::userspace_gntalloc(unsigned otherend)
: gntalloc(otherend)
, _gntalloc(file_desc::open("/dev/xen/gntalloc", O_RDWR)) {
}
struct ioctl_gntalloc_alloc_gref*
userspace_gntalloc::get_gref(unsigned nr_ents)
{
struct ioctl_gntalloc_alloc_gref *gref;
gref = static_cast<struct ioctl_gntalloc_alloc_gref *>(malloc(sizeof(*gref) + nr_ents * sizeof(gref->gref_ids[0])));
gref->domid = _otherend;
gref->flags = GNTALLOC_FLAG_WRITABLE;
gref->count = nr_ents;
_gntalloc.ioctl<struct ioctl_gntalloc_alloc_gref>(IOCTL_GNTALLOC_ALLOC_GREF, *gref);
return gref;
}
grant_head *userspace_gntalloc::alloc_ref(unsigned nr_ents) {
auto gref = get_gref(nr_ents);
_gntmap = _gntalloc.map_shared_rw(4096 * nr_ents, gref->index);
char *addr = _gntmap.get();
std::vector<gntref> v;
for (unsigned i = 0; i < nr_ents; ++i) {
auto ref = gref->gref_ids[i];
auto *page = addr + 4096 * i;
v.push_back({int(ref), page});
}
free(gref);
return new userspace_grant_head(v);
}
gntref userspace_gntalloc::alloc_ref() {
auto gref = get_gref(1);
// FIXME: unmap?
void *addr = _gntalloc.map_shared_rw(4096, gref->index).release();
auto p = gntref{int(gref->gref_ids[0]), addr};
free(gref);
return p;
}
gntref userspace_grant_head::new_ref() {
auto r = _refs.back();
_refs.pop_back();
return r;
}
gntref userspace_grant_head::new_ref(void *addr, size_t size) {
auto ref = _refs.back();
memcpy(ref.page, addr, size);
_refs.pop_back();
return ref;
}
void userspace_grant_head::free_ref(gntref& ref) {
_refs.push_back(ref);
ref = invalid_ref;
}
#ifdef HAVE_OSV
class kernel_gntalloc;
class kernel_grant_head : public grant_head {
uint32_t _head;
public:
kernel_grant_head(uint32_t head) : _head(head) {}
virtual gntref new_ref() override;
virtual gntref new_ref(void *addr, size_t size) override;
virtual void free_ref(gntref& ref);
};
class kernel_gntalloc : public gntalloc {
static constexpr int _tx_grants = 256;
static constexpr int _rx_grants = 256;
uint32_t tx_head, rx_head;
protected:
void *new_frame();
public:
kernel_gntalloc(unsigned otherend) : gntalloc(otherend) {}
virtual gntref alloc_ref() override;
virtual grant_head *alloc_ref(unsigned refs) override;
friend class kernel_grant_head;
};
// FIXME: It is not actually that far-fetched to run seastar ontop of raw pv,
// and in that case we'll need to have an extra step here
inline uint64_t
virt_to_mfn(void *virt) {
return virt_to_phys(virt) >> 12;
}
gntref kernel_grant_head::new_ref() {
auto gnt = dynamic_cast<kernel_gntalloc *>(gntalloc::instance());
auto ref = gnttab_claim_grant_reference(&_head);
auto page = gnt->new_frame();
gnttab_grant_foreign_access_ref(ref, gnt->_otherend, virt_to_mfn(page), 0);
return gntref(ref, page);
}
gntref kernel_grant_head::new_ref(void *addr, size_t size) {
auto gnt = dynamic_cast<kernel_gntalloc *>(gntalloc::instance());
// FIXME: if we can guarantee that the packet allocation came from malloc, not
// mmap, we can grant it directly, without copying. We would also have to propagate
// the offset information, but that is easier
auto page = gnt->new_frame();
memcpy(page, addr, size);
auto ref = gnttab_claim_grant_reference(&_head);
gnttab_grant_foreign_access_ref(ref, gnt->_otherend, virt_to_mfn(page), 0);
return gntref(ref, page);
}
void *kernel_gntalloc::new_frame() {
return aligned_alloc(4096, 4096);
}
gntref kernel_gntalloc::alloc_ref() {
void *page = new_frame();
uint32_t ref;
if (gnttab_grant_foreign_access(_otherend, virt_to_mfn(page), 0, &ref)) {
throw std::runtime_error("Failed to initialize allocate grant\n");
}
return gntref(int(ref), page);
}
grant_head *kernel_gntalloc::alloc_ref(unsigned nr_ents) {
std::vector<gntref> v;
uint32_t head;
if (gnttab_alloc_grant_references(nr_ents, &head)) {
throw std::runtime_error("Failed to initialize allocate grant\n");
}
return new kernel_grant_head(head);
}
void kernel_grant_head::free_ref(gntref& ref) {
gnttab_end_foreign_access_ref(ref.xen_id);
gnttab_release_grant_reference(&_head, ref.xen_id);
free(ref.page);
ref = invalid_ref;
}
#endif
gntalloc *gntalloc::_instance = nullptr;
gntalloc *gntalloc::instance(bool userspace, unsigned otherend) {
if (!_instance) {
#ifdef HAVE_OSV
if (!userspace) {
_instance = new kernel_gntalloc(otherend);
} else
#endif
_instance = new userspace_gntalloc(otherend);
}
return _instance;
}
gntalloc *gntalloc::instance() {
if (!_instance) {
throw std::runtime_error("Acquiring grant instance without specifying otherend: invalid context");
}
return _instance;
}
}