/**
@file
@brief Message transport between kernel and userspace
@details Copyright (c) 2017 Acronis International GmbH
@author Mikhail Krivtsov (mikhail.krivtsov@acronis.com)
@since $Id: $
*/
#pragma once
#include "compat.h"
#include "ring.h"
#include "set.h"
#include <linux/fs.h>
#include <linux/types.h> // bool, [u]int(8|16|32|64)_t, pid_t
#include "transport_id.h"
#include "transport_protocol.h"
// This is the header of the buffer that is 'mmap'd between kernel and user space.
// Both reader & write must use READ_ONCE/WRITE_ONCE/smp_* when accessing its contents.
typedef struct {
// 'head' and 'tail' is ring-like - reader reads from the 'head' and moves it to the 'tail'
// 'writer' moves the 'tail' as showing that more content is available.
// In our case, kernel=writer & userspace=reader
// Head is written only by userspace, it is specifying offset in 'entries' in bytes
// When head is written by userspace using 'smp_store_release', kernel must 'smp_load_acquire' it
uint32_t head ____cacheline_aligned_in_smp;
// Tail is written only by kernelspace, it is specifying offset in 'entries' in bytes
uint32_t tail ____cacheline_aligned_in_smp;
// Entries with the data, varsized struct where 'data_queue_entry_t' itself is varsized
data_queue_entry_t entries[] ____cacheline_aligned_in_smp;
} shared_data_queue_t;
typedef struct {
atomic_t refcount;
wait_queue_head_t msg_wait_queue;
} transport_event_t;
typedef struct {
atomic_t refcount;
pid_t control_tgid;
uint64_t events_mask;
uint64_t events_subtype_inclusion_mask;
uint64_t events_subtype_exclusion_mask;
spinlock_t msg_spinlock;
transport_event_t* event;
bool shutdown;
ring_t msg_ring;
// sent messages waiting for 'reply'
set_t sent_msgs_set;
uint32_t queue_size;
shared_data_queue_t *queue;
atomic_t queue_event;
transport_id_t transport_id;
uint64_t bytes_written;
} transport_t;
typedef struct {
pid_t control_tgid[MAX_TRANSPORT_SIZE];
transport_t *transports[MAX_TRANSPORT_SIZE];
} transports_t;
typedef struct {
struct mutex transport_mutex;
unsigned transport_count;
uint64_t combined_events_mask;
uint64_t combined_events_subtype_inclusion_mask;
uint64_t combined_events_subtype_exclusion_mask;
transports_t transports;
atomic64_t msg_id_sequence;
uint64_t last_transport_seq_num;
transport_ids_t transport_ids;
} transport_global_t;
// 'module' 'init'/'down'
int transport_mod_init(void);
void transport_mod_down(void);
// 'device' 'fops'
int transport_device_open(struct inode *, struct file *);
long transport_device_ioctl(struct file *, unsigned int, unsigned long);
ssize_t transport_device_read(struct file *, char __user *, size_t, loff_t *);
ssize_t transport_device_write(struct file *, const char __user *, size_t,
loff_t *);
int transport_device_release(struct inode *, struct file *);
int transport_device_mmap(struct file *filp, struct vm_area_struct *vma);
extern transport_global_t transport_global;
static inline uint64_t transport_global_get_combined_mask(void)
{
return READ_ONCE(transport_global.combined_events_mask);
}
static inline bool transport_global_subtype_needed(uint64_t subtype)
{
if (!(READ_ONCE(transport_global.combined_events_subtype_inclusion_mask) & subtype)) {
return false;
}
if (READ_ONCE(transport_global.combined_events_subtype_exclusion_mask) & subtype) {
return false;
}
return true;
}
static inline void transport_global_get_ids(transport_ids_t *ids)
{
int i;
for (i = 0; i < MAX_TRANSPORT_SIZE; i++) {
ids->ids[i] = READ_ONCE(transport_global.transport_ids.ids[i]);
}
}
static inline bool transport_is_control_tgid(pid_t tgid)
{
int i;
for (i = 0; i < MAX_TRANSPORT_SIZE; i++) {
if (READ_ONCE(transport_global.transports.control_tgid[i]) == tgid)
return true;
}
return false;
}
static inline bool transport_process_belongs_to_control(struct task_struct* tsk)
{
struct task_struct* parent;
pid_t parent_tgid = 0;
if (transport_is_control_tgid(tsk->tgid))
return true;
rcu_read_lock();
parent = rcu_dereference(tsk->real_parent);
if (parent) {
parent_tgid = parent->tgid;
}
rcu_read_unlock();
if (parent && transport_is_control_tgid(parent_tgid))
return true;
return false;
}
static inline msg_id_t transport_global_sequence_next(void)
{
return 1 + atomic64_inc_return(&transport_global.msg_id_sequence);
}
struct msg_s;
void send_msg_async(struct msg_s *msg);
void send_msg_sync(struct msg_s *msg);
void send_msg_async_unref_unchecked(struct msg_s *msg);
void send_msg_sync_unref_unchecked(struct msg_s *msg);
static inline void send_msg_async_unref(struct msg_s *msg) {
if (msg)
send_msg_async_unref_unchecked(msg);
}
static inline void send_msg_sync_unref(struct msg_s *msg) {
if (msg)
send_msg_sync_unref_unchecked(msg);
}
|