/*
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2002-2015, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*
* Copyright 2001, Mark-Jan Bastian. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
/*! Ports for IPC */
#include <port.h>
#include <algorithm>
#include <ctype.h>
#include <iovec.h>
#include <stdlib.h>
#include <string.h>
#include <OS.h>
#include <AutoDeleter.h>
#include <arch/int.h>
#include <heap.h>
#include <kernel.h>
#include <Notifications.h>
#include <sem.h>
#include <syscall_restart.h>
#include <team.h>
#include <tracing.h>
#include <util/AutoLock.h>
#include <util/list.h>
#include <vm/vm.h>
#include <wait_for_objects.h>
//#define TRACE_PORTS
#ifdef TRACE_PORTS
# define TRACE(x) dprintf x
#else
# define TRACE(x)
#endif
#if __GNUC__ >= 3
# define GCC_2_NRV(x)
// GCC >= 3.1 doesn't need it anymore
#else
# define GCC_2_NRV(x) return x;
// GCC 2 named return value syntax
// see http://gcc.gnu.org/onlinedocs/gcc-2.95.2/gcc_5.html#SEC106
#endif
// Locking:
// * sPortsLock: Protects the sPorts and sPortsByName hash tables.
// * sTeamListLock[]: Protects Team::port_list. Lock index for given team is
// (Team::id % kTeamListLockCount).
// * Port::lock: Protects all Port members save team_link, hash_link, lock and
// state. id is immutable.
//
// Port::state ensures atomicity by providing a linearization point for adding
// and removing ports to the hash tables and the team port list.
// * sPortsLock and sTeamListLock[] are locked separately and not in a nested
// fashion, so a port can be in the hash table but not in the team port list
// or vice versa. => Without further provisions, insertion and removal are
// not linearizable and thus not concurrency-safe.
// * To make insertion and removal linearizable, Port::state was added. It is
// always only accessed atomically and updates are done using
// atomic_test_and_set(). A port is only seen as existent when its state is
// Port::kActive.
// * Deletion of ports is done in two steps: logical and physical deletion.
// First, logical deletion happens and sets Port::state to Port::kDeleted.
// This is an atomic operation and from then on, functions like
// get_locked_port() consider this port as deleted and ignore it. Secondly,
// physical deletion removes the port from hash tables and team port list.
// In a similar way, port creation first inserts into hashes and team list
// and only then sets port to Port::kActive.
// This creates a linearization point at the atomic update of Port::state,
// operations become linearizable and thus concurrency-safe. To help
// understanding, the linearization points are annotated with comments.
// * Ports are reference-counted so it's not a problem when someone still
// has a reference to a deleted port.
namespace {
struct port_message : DoublyLinkedListLinkImpl<port_message> {
int32 code;
size_t size;
uid_t sender;
gid_t sender_group;
team_id sender_team;
char buffer[0];
};
typedef DoublyLinkedList<port_message> MessageList;
} // namespace
static void put_port_message(port_message* message);
namespace {
struct Port : public KernelReferenceable {
enum State {
kUnused = 0,
kActive,
kDeleted
};
struct list_link team_link;
Port* hash_link;
port_id id;
team_id owner;
Port* name_hash_link;
size_t name_hash;
int32 capacity;
mutex lock;
int32 state;
uint32 read_count;
int32 write_count;
ConditionVariable read_condition;
ConditionVariable write_condition;
int32 total_count;
// messages read from port since creation
select_info* select_infos;
MessageList messages;
Port(team_id owner, int32 queueLength, char* name)
:
owner(owner),
name_hash(0),
capacity(queueLength),
state(kUnused),
read_count(0),
write_count(queueLength),
total_count(0),
select_infos(NULL)
{
// id is initialized when the caller adds the port to the hash table
mutex_init(&lock, name);
read_condition.Init(this, "port read");
write_condition.Init(this, "port write");
}
virtual ~Port()
{
while (port_message* message = messages.RemoveHead())
put_port_message(message);
free((char*)lock.name);
lock.name = NULL;
}
};
struct PortHashDefinition {
typedef port_id KeyType;
typedef Port ValueType;
size_t HashKey(port_id key) const
{
return key;
}
size_t Hash(Port* value) const
{
return HashKey(value->id);
}
bool Compare(port_id key, Port* value) const
{
return value->id == key;
}
Port*& GetLink(Port* value) const
{
return value->hash_link;
}
};
typedef BOpenHashTable<PortHashDefinition> PortHashTable;
struct PortNameHashDefinition {
typedef const char* KeyType;
typedef Port ValueType;
size_t HashKey(const char* key) const
{
// Hash function: hash(key) = key[0] * 31^(length - 1)
// + key[1] * 31^(length - 2) + ... + key[length - 1]
const size_t length = strlen(key);
size_t hash = 0;
for (size_t index = 0; index < length; index++)
hash = 31 * hash + key[index];
return hash;
}
size_t Hash(Port* value) const
{
size_t& hash = value->name_hash;
if (hash == 0)
hash = HashKey(value->lock.name);
return hash;
}
bool Compare(const char* key, Port* value) const
{
return (strcmp(key, value->lock.name) == 0);
}
Port*& GetLink(Port* value) const
{
return value->name_hash_link;
}
};
typedef BOpenHashTable<PortNameHashDefinition> PortNameHashTable;
class PortNotificationService : public DefaultNotificationService {
public:
PortNotificationService();
void Notify(uint32 opcode, port_id team);
};
} // namespace
// #pragma mark - tracing
#if PORT_TRACING
namespace PortTracing {
class Create : public AbstractTraceEntry {
public:
Create(Port* port)
:
fID(port->id),
fOwner(port->owner),
fCapacity(port->capacity)
{
fName = alloc_tracing_buffer_strcpy(port->lock.name, B_OS_NAME_LENGTH,
false);
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("port %ld created, name \"%s\", owner %ld, capacity %ld",
fID, fName, fOwner, fCapacity);
}
private:
port_id fID;
char* fName;
team_id fOwner;
int32 fCapacity;
};
class Delete : public AbstractTraceEntry {
public:
Delete(Port* port)
:
fID(port->id)
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("port %ld deleted", fID);
}
private:
port_id fID;
};
class Read : public AbstractTraceEntry {
public:
Read(const BReference<Port>& portRef, int32 code, ssize_t result)
:
fID(portRef->id),
fReadCount(portRef->read_count),
fWriteCount(portRef->write_count),
fCode(code),
fResult(result)
{
Initialized();
}
Read(port_id id, int32 readCount, int32 writeCount, int32 code,
ssize_t result)
:
fID(id),
fReadCount(readCount),
fWriteCount(writeCount),
fCode(code),
fResult(result)
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("port %ld read, read %ld, write %ld, code %lx: %ld",
fID, fReadCount, fWriteCount, fCode, fResult);
}
private:
port_id fID;
int32 fReadCount;
int32 fWriteCount;
int32 fCode;
ssize_t fResult;
};
class Write : public AbstractTraceEntry {
public:
Write(port_id id, int32 readCount, int32 writeCount, int32 code,
size_t bufferSize, ssize_t result)
:
fID(id),
fReadCount(readCount),
fWriteCount(writeCount),
fCode(code),
fBufferSize(bufferSize),
fResult(result)
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("port %ld write, read %ld, write %ld, code %lx, size %ld: %ld",
fID, fReadCount, fWriteCount, fCode, fBufferSize, fResult);
}
private:
port_id fID;
int32 fReadCount;
int32 fWriteCount;
int32 fCode;
size_t fBufferSize;
ssize_t fResult;
};
class Info : public AbstractTraceEntry {
public:
Info(const BReference<Port>& portRef, int32 code, ssize_t result)
:
fID(portRef->id),
fReadCount(portRef->read_count),
fWriteCount(portRef->write_count),
fCode(code),
fResult(result)
{
Initialized();
}
Info(port_id id, int32 readCount, int32 writeCount, int32 code,
ssize_t result)
:
fID(id),
fReadCount(readCount),
fWriteCount(writeCount),
fCode(code),
fResult(result)
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("port %ld info, read %ld, write %ld, code %lx: %ld",
fID, fReadCount, fWriteCount, fCode, fResult);
}
private:
port_id fID;
int32 fReadCount;
int32 fWriteCount;
int32 fCode;
ssize_t fResult;
};
class OwnerChange : public AbstractTraceEntry {
public:
OwnerChange(Port* port, team_id newOwner, status_t status)
:
fID(port->id),
fOldOwner(port->owner),
fNewOwner(newOwner),
fStatus(status)
{
Initialized();
}
virtual void AddDump(TraceOutput& out)
{
out.Print("port %ld owner change from %ld to %ld: %s", fID, fOldOwner,
fNewOwner, strerror(fStatus));
}
private:
port_id fID;
team_id fOldOwner;
team_id fNewOwner;
status_t fStatus;
};
} // namespace PortTracing
# define T(x) new(std::nothrow) PortTracing::x;
#else
# define T(x) ;
#endif
static const size_t kInitialPortBufferSize = 4 * 1024 * 1024;
static const size_t kTotalSpaceLimit = 64 * 1024 * 1024;
static const size_t kTeamSpaceLimit = 8 * 1024 * 1024;
static const size_t kBufferGrowRate = kInitialPortBufferSize;
#define MAX_QUEUE_LENGTH 4096
#define PORT_MAX_MESSAGE_SIZE (256 * 1024)
static int32 sMaxPorts = 4096;
static int32 sUsedPorts;
static PortHashTable sPorts;
static PortNameHashTable sPortsByName;
static ConditionVariable sNoSpaceCondition;
static int32 sTotalSpaceCommited;
static int32 sWaitingForSpace;
static port_id sNextPortID = 1;
static bool sPortsActive = false;
static rw_lock sPortsLock = RW_LOCK_INITIALIZER("ports list");
enum {
kTeamListLockCount = 8
};
static mutex sTeamListLock[kTeamListLockCount] = {
MUTEX_INITIALIZER("team ports list 1"),
MUTEX_INITIALIZER("team ports list 2"),
MUTEX_INITIALIZER("team ports list 3"),
MUTEX_INITIALIZER("team ports list 4"),
MUTEX_INITIALIZER("team ports list 5"),
MUTEX_INITIALIZER("team ports list 6"),
MUTEX_INITIALIZER("team ports list 7"),
MUTEX_INITIALIZER("team ports list 8")
};
static PortNotificationService sNotificationService;
// #pragma mark - TeamNotificationService
PortNotificationService::PortNotificationService()
:
DefaultNotificationService("ports")
{
}
void
PortNotificationService::Notify(uint32 opcode, port_id port)
{
char eventBuffer[128];
KMessage event;
event.SetTo(eventBuffer, sizeof(eventBuffer), PORT_MONITOR);
event.AddInt32("event", opcode);
event.AddInt32("port", port);
DefaultNotificationService::Notify(event, opcode);
}
// #pragma mark - debugger commands
static int
dump_port_list(int argc, char** argv)
{
const char* name = NULL;
team_id owner = -1;
if (argc > 2) {
if (!strcmp(argv[1], "team") || !strcmp(argv[1], "owner"))
owner = strtoul(argv[2], NULL, 0);
else if (!strcmp(argv[1], "name"))
name = argv[2];
} else if (argc > 1)
owner = strtoul(argv[1], NULL, 0);
kprintf("port id cap read-cnt write-cnt total team "
"name\n");
for (PortHashTable::Iterator it = sPorts.GetIterator();
Port* port = it.Next();) {
if ((owner != -1 && port->owner != owner)
|| (name != NULL && strstr(port->lock.name, name) == NULL))
continue;
kprintf("%p %8" B_PRId32 " %4" B_PRId32 " %9" B_PRIu32 " %9" B_PRId32
" %8" B_PRId32 " %6" B_PRId32 " %s\n", port, port->id,
port->capacity, port->read_count, port->write_count,
port->total_count, port->owner, port->lock.name);
}
return 0;
}
static void
_dump_port_info(Port* port)
{
kprintf("PORT: %p\n", port);
kprintf(" id: %" B_PRId32 "\n", port->id);
kprintf(" name: \"%s\"\n", port->lock.name);
kprintf(" owner: %" B_PRId32 "\n", port->owner);
kprintf(" capacity: %" B_PRId32 "\n", port->capacity);
kprintf(" read_count: %" B_PRIu32 "\n", port->read_count);
kprintf(" write_count: %" B_PRId32 "\n", port->write_count);
kprintf(" total count: %" B_PRId32 "\n", port->total_count);
if (!port->messages.IsEmpty()) {
kprintf("messages:\n");
MessageList::Iterator iterator = port->messages.GetIterator();
while (port_message* message = iterator.Next()) {
kprintf(" %p %08" B_PRIx32 " %ld\n", message, message->code, message->size);
}
}
set_debug_variable("_port", (addr_t)port);
set_debug_variable("_portID", port->id);
set_debug_variable("_owner", port->owner);
}
static int
dump_port_info(int argc, char** argv)
{
ConditionVariable* condition = NULL;
const char* name = NULL;
if (argc < 2) {
print_debugger_command_usage(argv[0]);
return 0;
}
if (argc > 2) {
if (!strcmp(argv[1], "address")) {
_dump_port_info((Port*)parse_expression(argv[2]));
return 0;
} else if (!strcmp(argv[1], "condition"))
condition = (ConditionVariable*)parse_expression(argv[2]);
else if (!strcmp(argv[1], "name"))
name = argv[2];
} else if (parse_expression(argv[1]) > 0) {
// if the argument looks like a number, treat it as such
int32 num = parse_expression(argv[1]);
Port* port = sPorts.Lookup(num);
if (port == NULL || port->state != Port::kActive) {
kprintf("port %" B_PRId32 " (%#" B_PRIx32 ") doesn't exist!\n",
num, num);
return 0;
}
_dump_port_info(port);
return 0;
} else
name = argv[1];
// walk through the ports list, trying to match name
for (PortHashTable::Iterator it = sPorts.GetIterator();
Port* port = it.Next();) {
if ((name != NULL && port->lock.name != NULL
&& !strcmp(name, port->lock.name))
|| (condition != NULL && (&port->read_condition == condition
|| &port->write_condition == condition))) {
_dump_port_info(port);
return 0;
}
}
return 0;
}
// #pragma mark - internal helper functions
/*! Notifies the port's select events.
The port must be locked.
*/
static void
notify_port_select_events(Port* port, uint16 events)
{
if (port->select_infos)
notify_select_events_list(port->select_infos, events);
}
static BReference<Port>
get_locked_port(port_id id) GCC_2_NRV(portRef)
{
#if __GNUC__ >= 3
BReference<Port> portRef;
#endif
{
ReadLocker portsLocker(sPortsLock);
portRef.SetTo(sPorts.Lookup(id));
}
if (portRef != NULL && portRef->state == Port::kActive)
mutex_lock(&portRef->lock);
else
portRef.Unset();
return portRef;
}
static BReference<Port>
get_port(port_id id) GCC_2_NRV(portRef)
{
#if __GNUC__ >= 3
BReference<Port> portRef;
#endif
ReadLocker portsLocker(sPortsLock);
portRef.SetTo(sPorts.Lookup(id));
return portRef;
}
/*! You need to own the port's lock when calling this function */
static inline bool
is_port_closed(Port* port)
{
return port->capacity == 0;
}
static void
put_port_message(port_message* message)
{
const size_t size = sizeof(port_message) + message->size;
free(message);
atomic_add(&sTotalSpaceCommited, -size);
if (sWaitingForSpace > 0)
sNoSpaceCondition.NotifyAll();
}
/*! Port must be locked. */
static status_t
get_port_message(int32 code, size_t bufferSize, uint32 flags, bigtime_t timeout,
port_message** _message, Port& port)
{
const size_t size = sizeof(port_message) + bufferSize;
while (true) {
int32 previouslyCommited = atomic_add(&sTotalSpaceCommited, size);
while (previouslyCommited + size > kTotalSpaceLimit) {
// TODO: add per team limit
// We are not allowed to allocate more memory, as our
// space limit has been reached - just wait until we get
// some free space again.
atomic_add(&sTotalSpaceCommited, -size);
// TODO: we don't want to wait - but does that also mean we
// shouldn't wait for free memory?
if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
return B_WOULD_BLOCK;
ConditionVariableEntry entry;
sNoSpaceCondition.Add(&entry);
port_id portID = port.id;
mutex_unlock(&port.lock);
atomic_add(&sWaitingForSpace, 1);
// TODO: right here the condition could be notified and we'd
// miss it.
status_t status = entry.Wait(flags, timeout);
atomic_add(&sWaitingForSpace, -1);
// re-lock the port
BReference<Port> newPortRef = get_locked_port(portID);
if (newPortRef.Get() != &port || is_port_closed(&port)) {
// the port is no longer usable
return B_BAD_PORT_ID;
}
if (status == B_TIMED_OUT)
return B_TIMED_OUT;
previouslyCommited = atomic_add(&sTotalSpaceCommited, size);
continue;
}
// Quota is fulfilled, try to allocate the buffer
port_message* message = (port_message*)malloc(size);
if (message != NULL) {
message->code = code;
message->size = bufferSize;
*_message = message;
return B_OK;
}
// We weren't able to allocate and we'll start over,so we remove our
// size from the commited-counter again.
atomic_add(&sTotalSpaceCommited, -size);
continue;
}
}
/*! Fills the port_info structure with information from the specified
port.
The port's lock must be held when called.
*/
static void
fill_port_info(Port* port, port_info* info, size_t size)
{
info->port = port->id;
info->team = port->owner;
info->capacity = port->capacity;
info->queue_count = port->read_count;
info->total_count = port->total_count;
strlcpy(info->name, port->lock.name, B_OS_NAME_LENGTH);
}
static ssize_t
copy_port_message(port_message* message, int32* _code, void* buffer,
size_t bufferSize, bool userCopy)
{
// check output buffer size
size_t size = std::min(bufferSize, message->size);
// copy message
if (_code != NULL)
*_code = message->code;
if (size > 0) {
if (userCopy) {
status_t status = user_memcpy(buffer, message->buffer, size);
if (status != B_OK)
return status;
} else
memcpy(buffer, message->buffer, size);
}
return size;
}
static void
uninit_port(Port* port)
{
MutexLocker locker(port->lock);
notify_port_select_events(port, B_EVENT_INVALID);
port->select_infos = NULL;
// Release the threads that were blocking on this port.
// read_port() will see the B_BAD_PORT_ID return value, and act accordingly
port->read_condition.NotifyAll(B_BAD_PORT_ID);
port->write_condition.NotifyAll(B_BAD_PORT_ID);
sNotificationService.Notify(PORT_REMOVED, port->id);
}
/*! Caller must ensure there is still a reference to the port. (Either by
* holding a reference itself or by holding a lock on one of the data
* structures in which it is referenced.)
*/
static status_t
delete_port_logical(Port* port)
{
for (;;) {
// Try to logically delete
const int32 oldState = atomic_test_and_set(&port->state,
Port::kDeleted, Port::kActive);
// Linearization point for port deletion
switch (oldState) {
case Port::kActive:
// Logical deletion succesful
return B_OK;
case Port::kDeleted:
// Someone else already deleted it in the meantime
TRACE(("delete_port_logical: already deleted port_id %ld\n",
port->id));
return B_BAD_PORT_ID;
case Port::kUnused:
// Port is still being created, retry
continue;
default:
// Port state got corrupted somehow
panic("Invalid port state!\n");
}
}
}
// #pragma mark - private kernel API
/*! This function deletes all the ports that are owned by the passed team.
*/
void
delete_owned_ports(Team* team)
{
TRACE(("delete_owned_ports(owner = %ld)\n", team->id));
list deletionList;
list_init_etc(&deletionList, port_team_link_offset());
const uint8 lockIndex = team->id % kTeamListLockCount;
MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
// Try to logically delete all ports from the team's port list.
// On success, move the port to deletionList.
Port* port = (Port*)list_get_first_item(&team->port_list);
while (port != NULL) {
status_t status = delete_port_logical(port);
// Contains linearization point
Port* nextPort = (Port*)list_get_next_item(&team->port_list, port);
if (status == B_OK) {
list_remove_link(&port->team_link);
list_add_item(&deletionList, port);
}
port = nextPort;
}
teamPortsListLocker.Unlock();
// Remove all ports in deletionList from hashes
{
WriteLocker portsLocker(sPortsLock);
for (Port* port = (Port*)list_get_first_item(&deletionList);
port != NULL;
port = (Port*)list_get_next_item(&deletionList, port)) {
sPorts.Remove(port);
sPortsByName.Remove(port);
port->ReleaseReference();
// joint reference for sPorts and sPortsByName
}
}
// Uninitialize ports and release team port list references
while (Port* port = (Port*)list_remove_head_item(&deletionList)) {
atomic_add(&sUsedPorts, -1);
uninit_port(port);
port->ReleaseReference();
// Reference for team port list
}
}
int32
port_max_ports(void)
{
return sMaxPorts;
}
int32
port_used_ports(void)
{
return sUsedPorts;
}
size_t
port_team_link_offset()
{
// Somewhat ugly workaround since we cannot use offsetof() for a class
// with vtable (GCC4 throws a warning then).
Port* port = (Port*)0;
return (size_t)&port->team_link;
}
status_t
port_init(kernel_args *args)
{
// initialize ports table and by-name hash
new(&sPorts) PortHashTable;
if (sPorts.Init() != B_OK) {
panic("Failed to init port hash table!");
return B_NO_MEMORY;
}
new(&sPortsByName) PortNameHashTable;
if (sPortsByName.Init() != B_OK) {
panic("Failed to init port by name hash table!");
return B_NO_MEMORY;
}
sNoSpaceCondition.Init(&sPorts, "port space");
// add debugger commands
add_debugger_command_etc("ports", &dump_port_list,
"Dump a list of all active ports (for team, with name, etc.)",
"[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]\n"
"Prints a list of all active ports meeting the given\n"
"requirement. If no argument is given, all ports are listed.\n"
" <team> - The team owning the ports.\n"
" <name> - Part of the name of the ports.\n", 0);
add_debugger_command_etc("port", &dump_port_info,
"Dump info about a particular port",
"(<id> | [ \"address\" ] <address>) | ([ \"name\" ] <name>) "
"| (\"condition\" <address>)\n"
"Prints info about the specified port.\n"
" <address> - Pointer to the port structure.\n"
" <name> - Name of the port.\n"
" <condition> - address of the port's read or write condition.\n", 0);
new(&sNotificationService) PortNotificationService();
sNotificationService.Register();
sPortsActive = true;
return B_OK;
}
// #pragma mark - public kernel API
port_id
create_port(int32 queueLength, const char* name)
{
TRACE(("create_port(queueLength = %ld, name = \"%s\")\n", queueLength,
name));
if (!sPortsActive) {
panic("ports used too early!\n");
return B_BAD_PORT_ID;
}
if (queueLength < 1 || queueLength > MAX_QUEUE_LENGTH)
return B_BAD_VALUE;
Team* team = thread_get_current_thread()->team;
if (team == NULL)
return B_BAD_TEAM_ID;
// check & dup name
char* nameBuffer = strdup(name != NULL ? name : "unnamed port");
if (nameBuffer == NULL)
return B_NO_MEMORY;
// create a port
Port* port = new(std::nothrow) Port(team_get_current_team_id(), queueLength,
nameBuffer);
if (port == NULL) {
free(nameBuffer);
return B_NO_MEMORY;
}
// check the ports limit
const int32 previouslyUsed = atomic_add(&sUsedPorts, 1);
if (previouslyUsed + 1 >= sMaxPorts) {
atomic_add(&sUsedPorts, -1);
delete port;
return B_NO_MORE_PORTS;
}
{
WriteLocker locker(sPortsLock);
// allocate a port ID
do {
port->id = sNextPortID++;
// handle integer overflow
if (sNextPortID < 0)
sNextPortID = 1;
} while (sPorts.Lookup(port->id) != NULL);
// Insert port physically:
// (1/2) Insert into hash tables
port->AcquireReference();
// joint reference for sPorts and sPortsByName
sPorts.Insert(port);
sPortsByName.Insert(port);
}
// (2/2) Insert into team list
{
const uint8 lockIndex = port->owner % kTeamListLockCount;
MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
port->AcquireReference();
list_add_item(&team->port_list, port);
}
// tracing, notifications, etc.
T(Create(port));
const port_id id = port->id;
// Insert port logically by marking it active
const int32 oldState = atomic_test_and_set(&port->state,
Port::kActive, Port::kUnused);
// Linearization point for port creation
if (oldState != Port::kUnused) {
// Nobody is allowed to tamper with the port before it's active.
panic("Port state was modified during creation!\n");
}
TRACE(("create_port() done: port created %ld\n", id));
sNotificationService.Notify(PORT_ADDED, id);
return id;
}
status_t
close_port(port_id id)
{
TRACE(("close_port(id = %ld)\n", id));
if (!sPortsActive || id < 0)
return B_BAD_PORT_ID;
// get the port
BReference<Port> portRef = get_locked_port(id);
if (portRef == NULL) {
TRACE(("close_port: invalid port_id %ld\n", id));
return B_BAD_PORT_ID;
}
MutexLocker lock(&portRef->lock, true);
// mark port to disable writing - deleting the semaphores will
// wake up waiting read/writes
portRef->capacity = 0;
notify_port_select_events(portRef, B_EVENT_INVALID);
portRef->select_infos = NULL;
portRef->read_condition.NotifyAll(B_BAD_PORT_ID);
portRef->write_condition.NotifyAll(B_BAD_PORT_ID);
return B_OK;
}
status_t
delete_port(port_id id)
{
TRACE(("delete_port(id = %ld)\n", id));
if (!sPortsActive || id < 0)
return B_BAD_PORT_ID;
BReference<Port> portRef = get_port(id);
if (portRef == NULL) {
TRACE(("delete_port: invalid port_id %ld\n", id));
return B_BAD_PORT_ID;
}
status_t status = delete_port_logical(portRef);
// Contains linearization point
if (status != B_OK)
return status;
// Now remove port physically:
// (1/2) Remove from hash tables
{
WriteLocker portsLocker(sPortsLock);
sPorts.Remove(portRef);
sPortsByName.Remove(portRef);
portRef->ReleaseReference();
// joint reference for sPorts and sPortsByName
}
// (2/2) Remove from team port list
{
const uint8 lockIndex = portRef->owner % kTeamListLockCount;
MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
list_remove_link(&portRef->team_link);
portRef->ReleaseReference();
}
uninit_port(portRef);
T(Delete(portRef));
atomic_add(&sUsedPorts, -1);
return B_OK;
}
status_t
select_port(int32 id, struct select_info* info, bool kernel)
{
if (id < 0)
return B_BAD_PORT_ID;
// get the port
BReference<Port> portRef = get_locked_port(id);
if (portRef == NULL)
return B_BAD_PORT_ID;
MutexLocker locker(portRef->lock, true);
// port must not yet be closed
if (is_port_closed(portRef))
return B_BAD_PORT_ID;
if (!kernel && portRef->owner == team_get_kernel_team_id()) {
// kernel port, but call from userland
return B_NOT_ALLOWED;
}
info->selected_events &= B_EVENT_READ | B_EVENT_WRITE | B_EVENT_INVALID;
if (info->selected_events != 0) {
uint16 events = 0;
info->next = portRef->select_infos;
portRef->select_infos = info;
// check for events
if ((info->selected_events & B_EVENT_READ) != 0
&& !portRef->messages.IsEmpty()) {
events |= B_EVENT_READ;
}
if (portRef->write_count > 0)
events |= B_EVENT_WRITE;
if (events != 0)
notify_select_events(info, events);
}
return B_OK;
}
status_t
deselect_port(int32 id, struct select_info* info, bool kernel)
{
if (id < 0)
return B_BAD_PORT_ID;
if (info->selected_events == 0)
return B_OK;
// get the port
BReference<Port> portRef = get_locked_port(id);
if (portRef == NULL)
return B_BAD_PORT_ID;
MutexLocker locker(portRef->lock, true);
// find and remove the infos
select_info** infoLocation = &portRef->select_infos;
while (*infoLocation != NULL && *infoLocation != info)
infoLocation = &(*infoLocation)->next;
if (*infoLocation == info)
*infoLocation = info->next;
return B_OK;
}
port_id
find_port(const char* name)
{
TRACE(("find_port(name = \"%s\")\n", name));
if (!sPortsActive) {
panic("ports used too early!\n");
return B_NAME_NOT_FOUND;
}
if (name == NULL)
return B_BAD_VALUE;
ReadLocker locker(sPortsLock);
Port* port = sPortsByName.Lookup(name);
// Since we have sPortsLock and don't return the port itself,
// no BReference necessary
if (port != NULL && port->state == Port::kActive)
return port->id;
return B_NAME_NOT_FOUND;
}
status_t
_get_port_info(port_id id, port_info* info, size_t size)
{
TRACE(("get_port_info(id = %ld)\n", id));
if (info == NULL || size != sizeof(port_info))
return B_BAD_VALUE;
if (!sPortsActive || id < 0)
return B_BAD_PORT_ID;
// get the port
BReference<Port> portRef = get_locked_port(id);
if (portRef == NULL) {
TRACE(("get_port_info: invalid port_id %ld\n", id));
return B_BAD_PORT_ID;
}
MutexLocker locker(portRef->lock, true);
// fill a port_info struct with info
fill_port_info(portRef, info, size);
return B_OK;
}
status_t
_get_next_port_info(team_id teamID, int32* _cookie, struct port_info* info,
size_t size)
{
TRACE(("get_next_port_info(team = %ld)\n", teamID));
if (info == NULL || size != sizeof(port_info) || _cookie == NULL
|| teamID < 0) {
return B_BAD_VALUE;
}
if (!sPortsActive)
return B_BAD_PORT_ID;
Team* team = Team::Get(teamID);
if (team == NULL)
return B_BAD_TEAM_ID;
BReference<Team> teamReference(team, true);
// iterate through the team's port list
const uint8 lockIndex = teamID % kTeamListLockCount;
MutexLocker teamPortsListLocker(sTeamListLock[lockIndex]);
int32 stopIndex = *_cookie;
int32 index = 0;
Port* port = (Port*)list_get_first_item(&team->port_list);
while (port != NULL) {
if (!is_port_closed(port)) {
if (index == stopIndex)
break;
index++;
}
port = (Port*)list_get_next_item(&team->port_list, port);
}
if (port == NULL)
return B_BAD_PORT_ID;
// fill in the port info
BReference<Port> portRef = port;
teamPortsListLocker.Unlock();
// Only use portRef below this line...
MutexLocker locker(portRef->lock);
fill_port_info(portRef, info, size);
*_cookie = stopIndex + 1;
return B_OK;
}
ssize_t
port_buffer_size(port_id id)
{
return port_buffer_size_etc(id, 0, 0);
}
ssize_t
port_buffer_size_etc(port_id id, uint32 flags, bigtime_t timeout)
{
port_message_info info;
status_t error = get_port_message_info_etc(id, &info, flags, timeout);
return error != B_OK ? error : info.size;
}
status_t
_get_port_message_info_etc(port_id id, port_message_info* info,
size_t infoSize, uint32 flags, bigtime_t timeout)
{
if (info == NULL || infoSize != sizeof(port_message_info))
return B_BAD_VALUE;
if (!sPortsActive || id < 0)
return B_BAD_PORT_ID;
flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
| B_ABSOLUTE_TIMEOUT;
// get the port
BReference<Port> portRef = get_locked_port(id);
if (portRef == NULL)
return B_BAD_PORT_ID;
MutexLocker locker(portRef->lock, true);
if (is_port_closed(portRef) && portRef->messages.IsEmpty()) {
T(Info(portRef, 0, B_BAD_PORT_ID));
TRACE(("_get_port_message_info_etc(): closed port %ld\n", id));
return B_BAD_PORT_ID;
}
while (portRef->read_count == 0) {
// We need to wait for a message to appear
if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
return B_WOULD_BLOCK;
ConditionVariableEntry entry;
portRef->read_condition.Add(&entry);
locker.Unlock();
// block if no message, or, if B_TIMEOUT flag set, block with timeout
status_t status = entry.Wait(flags, timeout);
if (status != B_OK) {
T(Info(portRef, 0, status));
return status;
}
// re-lock
BReference<Port> newPortRef = get_locked_port(id);
if (newPortRef == NULL) {
T(Info(id, 0, 0, 0, B_BAD_PORT_ID));
return B_BAD_PORT_ID;
}
locker.SetTo(newPortRef->lock, true);
if (newPortRef != portRef
|| (is_port_closed(portRef) && portRef->messages.IsEmpty())) {
// the port is no longer there
T(Info(id, 0, 0, 0, B_BAD_PORT_ID));
return B_BAD_PORT_ID;
}
}
// determine tail & get the length of the message
port_message* message = portRef->messages.Head();
if (message == NULL) {
panic("port %" B_PRId32 ": no messages found\n", portRef->id);
return B_ERROR;
}
info->size = message->size;
info->sender = message->sender;
info->sender_group = message->sender_group;
info->sender_team = message->sender_team;
T(Info(portRef, message->code, B_OK));
// notify next one, as we haven't read from the port
portRef->read_condition.NotifyOne();
return B_OK;
}
ssize_t
port_count(port_id id)
{
if (!sPortsActive || id < 0)
return B_BAD_PORT_ID;
// get the port
BReference<Port> portRef = get_locked_port(id);
if (portRef == NULL) {
TRACE(("port_count: invalid port_id %ld\n", id));
return B_BAD_PORT_ID;
}
MutexLocker locker(portRef->lock, true);
// return count of messages
return portRef->read_count;
}
ssize_t
read_port(port_id port, int32* msgCode, void* buffer, size_t bufferSize)
{
return read_port_etc(port, msgCode, buffer, bufferSize, 0, 0);
}
ssize_t
read_port_etc(port_id id, int32* _code, void* buffer, size_t bufferSize,
uint32 flags, bigtime_t timeout)
{
if (!sPortsActive || id < 0)
return B_BAD_PORT_ID;
if ((buffer == NULL && bufferSize > 0) || timeout < 0)
return B_BAD_VALUE;
bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0;
bool peekOnly = !userCopy && (flags & B_PEEK_PORT_MESSAGE) != 0;
// TODO: we could allow peeking for user apps now
flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
| B_ABSOLUTE_TIMEOUT;
// get the port
BReference<Port> portRef = get_locked_port(id);
if (portRef == NULL)
return B_BAD_PORT_ID;
MutexLocker locker(portRef->lock, true);
if (is_port_closed(portRef) && portRef->messages.IsEmpty()) {
T(Read(portRef, 0, B_BAD_PORT_ID));
TRACE(("read_port_etc(): closed port %ld\n", id));
return B_BAD_PORT_ID;
}
while (portRef->read_count == 0) {
if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
return B_WOULD_BLOCK;
// We need to wait for a message to appear
ConditionVariableEntry entry;
portRef->read_condition.Add(&entry);
locker.Unlock();
// block if no message, or, if B_TIMEOUT flag set, block with timeout
status_t status = entry.Wait(flags, timeout);
// re-lock
BReference<Port> newPortRef = get_locked_port(id);
if (newPortRef == NULL) {
T(Read(id, 0, 0, 0, B_BAD_PORT_ID));
return B_BAD_PORT_ID;
}
locker.SetTo(newPortRef->lock, true);
if (newPortRef != portRef
|| (is_port_closed(portRef) && portRef->messages.IsEmpty())) {
// the port is no longer there
T(Read(id, 0, 0, 0, B_BAD_PORT_ID));
return B_BAD_PORT_ID;
}
if (status != B_OK) {
T(Read(portRef, 0, status));
return status;
}
}
// determine tail & get the length of the message
port_message* message = portRef->messages.Head();
if (message == NULL) {
panic("port %" B_PRId32 ": no messages found\n", portRef->id);
return B_ERROR;
}
if (peekOnly) {
size_t size = copy_port_message(message, _code, buffer, bufferSize,
userCopy);
T(Read(portRef, message->code, size));
portRef->read_condition.NotifyOne();
// we only peeked, but didn't grab the message
return size;
}
portRef->messages.RemoveHead();
portRef->total_count++;
portRef->write_count++;
portRef->read_count--;
notify_port_select_events(portRef, B_EVENT_WRITE);
portRef->write_condition.NotifyOne();
// make one spot in queue available again for write
T(Read(portRef, message->code, std::min(bufferSize, message->size)));
locker.Unlock();
size_t size = copy_port_message(message, _code, buffer, bufferSize,
userCopy);
put_port_message(message);
return size;
}
status_t
write_port(port_id id, int32 msgCode, const void* buffer, size_t bufferSize)
{
iovec vec = { (void*)buffer, bufferSize };
return writev_port_etc(id, msgCode, &vec, 1, bufferSize, 0, 0);
}
status_t
write_port_etc(port_id id, int32 msgCode, const void* buffer,
size_t bufferSize, uint32 flags, bigtime_t timeout)
{
iovec vec = { (void*)buffer, bufferSize };
return writev_port_etc(id, msgCode, &vec, 1, bufferSize, flags, timeout);
}
status_t
writev_port_etc(port_id id, int32 msgCode, const iovec* msgVecs,
size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout)
{
if (!sPortsActive || id < 0)
return B_BAD_PORT_ID;
if (bufferSize > PORT_MAX_MESSAGE_SIZE)
return B_BAD_VALUE;
bool userCopy = (flags & PORT_FLAG_USE_USER_MEMCPY) != 0;
// mask irrelevant flags (for acquire_sem() usage)
flags &= B_CAN_INTERRUPT | B_KILL_CAN_INTERRUPT | B_RELATIVE_TIMEOUT
| B_ABSOLUTE_TIMEOUT;
if ((flags & B_RELATIVE_TIMEOUT) != 0
&& timeout != B_INFINITE_TIMEOUT && timeout > 0) {
// Make the timeout absolute, since we have more than one step where
// we might have to wait
flags = (flags & ~B_RELATIVE_TIMEOUT) | B_ABSOLUTE_TIMEOUT;
timeout += system_time();
}
status_t status;
port_message* message = NULL;
// get the port
BReference<Port> portRef = get_locked_port(id);
if (portRef == NULL) {
TRACE(("write_port_etc: invalid port_id %ld\n", id));
return B_BAD_PORT_ID;
}
MutexLocker locker(portRef->lock, true);
if (is_port_closed(portRef)) {
TRACE(("write_port_etc: port %ld closed\n", id));
return B_BAD_PORT_ID;
}
if (portRef->write_count <= 0) {
if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0)
return B_WOULD_BLOCK;
portRef->write_count--;
// We need to block in order to wait for a free message slot
ConditionVariableEntry entry;
portRef->write_condition.Add(&entry);
locker.Unlock();
status = entry.Wait(flags, timeout);
// re-lock
BReference<Port> newPortRef = get_locked_port(id);
if (newPortRef == NULL) {
T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID));
return B_BAD_PORT_ID;
}
locker.SetTo(newPortRef->lock, true);
if (newPortRef != portRef || is_port_closed(portRef)) {
// the port is no longer there
T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID));
return B_BAD_PORT_ID;
}
if (status != B_OK)
goto error;
} else
portRef->write_count--;
status = get_port_message(msgCode, bufferSize, flags, timeout,
&message, *portRef);
if (status != B_OK) {
if (status == B_BAD_PORT_ID) {
// the port had to be unlocked and is now no longer there
T(Write(id, 0, 0, 0, 0, B_BAD_PORT_ID));
return B_BAD_PORT_ID;
}
goto error;
}
// sender credentials
message->sender = geteuid();
message->sender_group = getegid();
message->sender_team = team_get_current_team_id();
if (bufferSize > 0) {
size_t offset = 0;
for (uint32 i = 0; i < vecCount; i++) {
size_t bytes = msgVecs[i].iov_len;
if (bytes > bufferSize)
bytes = bufferSize;
if (userCopy) {
status_t status = user_memcpy(message->buffer + offset,
msgVecs[i].iov_base, bytes);
if (status != B_OK) {
put_port_message(message);
goto error;
}
} else
memcpy(message->buffer + offset, msgVecs[i].iov_base, bytes);
bufferSize -= bytes;
if (bufferSize == 0)
break;
offset += bytes;
}
}
portRef->messages.Add(message);
portRef->read_count++;
T(Write(id, portRef->read_count, portRef->write_count, message->code,
message->size, B_OK));
notify_port_select_events(portRef, B_EVENT_READ);
portRef->read_condition.NotifyOne();
return B_OK;
error:
// Give up our slot in the queue again, and let someone else
// try and fail
T(Write(id, portRef->read_count, portRef->write_count, 0, 0, status));
portRef->write_count++;
notify_port_select_events(portRef, B_EVENT_WRITE);
portRef->write_condition.NotifyOne();
return status;
}
status_t
set_port_owner(port_id id, team_id newTeamID)
{
TRACE(("set_port_owner(id = %ld, team = %ld)\n", id, newTeamID));
if (id < 0)
return B_BAD_PORT_ID;
// get the new team
Team* team = Team::Get(newTeamID);
if (team == NULL)
return B_BAD_TEAM_ID;
BReference<Team> teamReference(team, true);
// get the port
BReference<Port> portRef = get_locked_port(id);
if (portRef == NULL) {
TRACE(("set_port_owner: invalid port_id %ld\n", id));
return B_BAD_PORT_ID;
}
MutexLocker locker(portRef->lock, true);
// transfer ownership to other team
if (team->id != portRef->owner) {
uint8 firstLockIndex = portRef->owner % kTeamListLockCount;
uint8 secondLockIndex = team->id % kTeamListLockCount;
// Avoid deadlocks: always lock lower index first
if (secondLockIndex < firstLockIndex) {
uint8 temp = secondLockIndex;
secondLockIndex = firstLockIndex;
firstLockIndex = temp;
}
MutexLocker oldTeamPortsListLocker(sTeamListLock[firstLockIndex]);
MutexLocker newTeamPortsListLocker;
if (firstLockIndex != secondLockIndex) {
newTeamPortsListLocker.SetTo(sTeamListLock[secondLockIndex],
false);
}
// Now that we have locked the team port lists, check the state again
if (portRef->state == Port::kActive) {
list_remove_link(&portRef->team_link);
list_add_item(&team->port_list, portRef.Get());
portRef->owner = team->id;
} else {
// Port was already deleted. We haven't changed anything yet so
// we can cancel the operation.
return B_BAD_PORT_ID;
}
}
T(OwnerChange(portRef, team->id, B_OK));
return B_OK;
}
// #pragma mark - syscalls
port_id
_user_create_port(int32 queueLength, const char *userName)
{
char name[B_OS_NAME_LENGTH];
if (userName == NULL)
return create_port(queueLength, NULL);
if (!IS_USER_ADDRESS(userName)
|| user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
return B_BAD_ADDRESS;
return create_port(queueLength, name);
}
status_t
_user_close_port(port_id id)
{
return close_port(id);
}
status_t
_user_delete_port(port_id id)
{
return delete_port(id);
}
port_id
_user_find_port(const char *userName)
{
char name[B_OS_NAME_LENGTH];
if (userName == NULL)
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userName)
|| user_strlcpy(name, userName, B_OS_NAME_LENGTH) < B_OK)
return B_BAD_ADDRESS;
return find_port(name);
}
status_t
_user_get_port_info(port_id id, struct port_info *userInfo)
{
struct port_info info;
status_t status;
if (userInfo == NULL)
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userInfo))
return B_BAD_ADDRESS;
status = get_port_info(id, &info);
// copy back to user space
if (status == B_OK
&& user_memcpy(userInfo, &info, sizeof(struct port_info)) < B_OK)
return B_BAD_ADDRESS;
return status;
}
status_t
_user_get_next_port_info(team_id team, int32 *userCookie,
struct port_info *userInfo)
{
struct port_info info;
status_t status;
int32 cookie;
if (userCookie == NULL || userInfo == NULL)
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userCookie) || !IS_USER_ADDRESS(userInfo)
|| user_memcpy(&cookie, userCookie, sizeof(int32)) < B_OK)
return B_BAD_ADDRESS;
status = get_next_port_info(team, &cookie, &info);
// copy back to user space
if (user_memcpy(userCookie, &cookie, sizeof(int32)) < B_OK
|| (status == B_OK && user_memcpy(userInfo, &info,
sizeof(struct port_info)) < B_OK))
return B_BAD_ADDRESS;
return status;
}
ssize_t
_user_port_buffer_size_etc(port_id port, uint32 flags, bigtime_t timeout)
{
syscall_restart_handle_timeout_pre(flags, timeout);
status_t status = port_buffer_size_etc(port, flags | B_CAN_INTERRUPT,
timeout);
return syscall_restart_handle_timeout_post(status, timeout);
}
ssize_t
_user_port_count(port_id port)
{
return port_count(port);
}
status_t
_user_set_port_owner(port_id port, team_id team)
{
return set_port_owner(port, team);
}
ssize_t
_user_read_port_etc(port_id port, int32 *userCode, void *userBuffer,
size_t bufferSize, uint32 flags, bigtime_t timeout)
{
int32 messageCode;
ssize_t bytesRead;
syscall_restart_handle_timeout_pre(flags, timeout);
if (userBuffer == NULL && bufferSize != 0)
return B_BAD_VALUE;
if ((userCode != NULL && !IS_USER_ADDRESS(userCode))
|| (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer)))
return B_BAD_ADDRESS;
bytesRead = read_port_etc(port, &messageCode, userBuffer, bufferSize,
flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
if (bytesRead >= 0 && userCode != NULL
&& user_memcpy(userCode, &messageCode, sizeof(int32)) < B_OK)
return B_BAD_ADDRESS;
return syscall_restart_handle_timeout_post(bytesRead, timeout);
}
status_t
_user_write_port_etc(port_id port, int32 messageCode, const void *userBuffer,
size_t bufferSize, uint32 flags, bigtime_t timeout)
{
iovec vec = { (void *)userBuffer, bufferSize };
syscall_restart_handle_timeout_pre(flags, timeout);
if (userBuffer == NULL && bufferSize != 0)
return B_BAD_VALUE;
if (userBuffer != NULL && !IS_USER_ADDRESS(userBuffer))
return B_BAD_ADDRESS;
status_t status = writev_port_etc(port, messageCode, &vec, 1, bufferSize,
flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT, timeout);
return syscall_restart_handle_timeout_post(status, timeout);
}
status_t
_user_writev_port_etc(port_id port, int32 messageCode, const iovec *userVecs,
size_t vecCount, size_t bufferSize, uint32 flags, bigtime_t timeout)
{
syscall_restart_handle_timeout_pre(flags, timeout);
if (userVecs == NULL && bufferSize != 0)
return B_BAD_VALUE;
if (userVecs != NULL && !IS_USER_ADDRESS(userVecs))
return B_BAD_ADDRESS;
iovec *vecs = NULL;
if (userVecs && vecCount != 0) {
vecs = (iovec*)malloc(sizeof(iovec) * vecCount);
if (vecs == NULL)
return B_NO_MEMORY;
if (user_memcpy(vecs, userVecs, sizeof(iovec) * vecCount) < B_OK) {
free(vecs);
return B_BAD_ADDRESS;
}
}
status_t status = writev_port_etc(port, messageCode, vecs, vecCount,
bufferSize, flags | PORT_FLAG_USE_USER_MEMCPY | B_CAN_INTERRUPT,
timeout);
free(vecs);
return syscall_restart_handle_timeout_post(status, timeout);
}
status_t
_user_get_port_message_info_etc(port_id port, port_message_info *userInfo,
size_t infoSize, uint32 flags, bigtime_t timeout)
{
if (userInfo == NULL || infoSize != sizeof(port_message_info))
return B_BAD_VALUE;
syscall_restart_handle_timeout_pre(flags, timeout);
port_message_info info;
status_t error = _get_port_message_info_etc(port, &info, sizeof(info),
flags | B_CAN_INTERRUPT, timeout);
// copy info to userland
if (error == B_OK && (!IS_USER_ADDRESS(userInfo)
|| user_memcpy(userInfo, &info, sizeof(info)) != B_OK)) {
error = B_BAD_ADDRESS;
}
return syscall_restart_handle_timeout_post(error, timeout);
}
↑ V522 Dereferencing of the null pointer 'port' might take place.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: team_link, hash_link, id, name_hash_link.