From 403bb11a742dd7fc3e9687fbdca898602b4fbac9 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 7 Jan 2015 21:02:36 +0100 Subject: [PATCH] Factor out reply related bits to reply.[ch] Move some code from connection.c to reply.c for better separation. Signed-off-by: Daniel Mack --- Makefile | 1 + connection.c | 208 +-------------------------------------------------- reply.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++ reply.h | 62 +++++++++++++++ 4 files changed, 262 insertions(+), 204 deletions(-) create mode 100644 reply.c create mode 100644 reply.h diff --git a/Makefile b/Makefile index 4449f7f..0e7dec4 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ kdbus$(EXT)-y := \ policy.o \ pool.o \ queue.o \ + reply.o \ util.o obj-m += kdbus$(EXT).o diff --git a/connection.c b/connection.c index 99e9c3f..64b8534 100644 --- a/connection.c +++ b/connection.c @@ -44,125 +44,13 @@ #include "notify.h" #include "policy.h" #include "pool.h" +#include "reply.h" #include "util.h" #include "queue.h" #define KDBUS_CONN_ACTIVE_BIAS (INT_MIN + 2) #define KDBUS_CONN_ACTIVE_NEW (INT_MIN + 1) -/** - * struct kdbus_reply - an entry of kdbus_conn's list of replies - * @kref: Ref-count of this object - * @entry: The entry of the connection's reply_list - * @reply_dst: The connection the reply will be sent to (method origin) - * @queue_entry: The queue entry item that is prepared by the replying - * connection - * @deadline_ns: The deadline of the reply, in nanoseconds - * @cookie: The cookie of the requesting message - * @name_id: ID of the well-known name the original msg was sent to - * @sync: The reply block is waiting for synchronous I/O - * @waiting: The condition to synchronously wait for - * @interrupted: The sync reply was left in an interrupted state - * @err: The error code for the synchronous reply - */ -struct kdbus_reply { - struct kref kref; - struct list_head entry; - struct kdbus_conn *reply_dst; - struct kdbus_queue_entry *queue_entry; - u64 deadline_ns; - u64 cookie; - u64 name_id; - bool sync:1; - bool waiting:1; - bool interrupted:1; - int err; -}; - -static struct kdbus_reply * -kdbus_reply_new(struct kdbus_conn *reply_dst, - const struct kdbus_msg *msg, - struct kdbus_name_entry *name_entry, - bool sync) -{ - struct kdbus_reply *r; - int ret = 0; - - if (atomic_inc_return(&reply_dst->request_count) > - KDBUS_CONN_MAX_REQUESTS_PENDING) { - ret = -EMLINK; - goto exit_dec_request_count; - } - - r = kzalloc(sizeof(*r), GFP_KERNEL); - if (!r) { - ret = -ENOMEM; - goto exit_dec_request_count; - } - - kref_init(&r->kref); - r->reply_dst = kdbus_conn_ref(reply_dst); - r->cookie = msg->cookie; - r->name_id = name_entry ? name_entry->name_id : 0; - r->deadline_ns = msg->timeout_ns; - - if (sync) { - r->sync = true; - r->waiting = true; - } - -exit_dec_request_count: - if (ret < 0) { - atomic_dec(&reply_dst->request_count); - return ERR_PTR(ret); - } - - return r; -} - -static void __kdbus_reply_free(struct kref *kref) -{ - struct kdbus_reply *reply = - container_of(kref, struct kdbus_reply, kref); - - atomic_dec(&reply->reply_dst->request_count); - kdbus_conn_unref(reply->reply_dst); - kfree(reply); -} - -static struct kdbus_reply* -kdbus_reply_ref(struct kdbus_reply *r) -{ - if (r) - kref_get(&r->kref); - return r; -} - -static struct kdbus_reply* -kdbus_reply_unref(struct kdbus_reply *r) -{ - if (r) - kref_put(&r->kref, __kdbus_reply_free); - return NULL; -} - -/* - * Remove the synchronous reply object from its connection - * reply_list, and wakeup remote peer (method origin) with the - * appropriate synchronous reply code - */ -static void kdbus_sync_reply_wakeup(struct kdbus_reply *reply, - int err) -{ - if (WARN_ON(!reply->sync)) - return; - - list_del_init(&reply->entry); - reply->waiting = false; - reply->err = err; - wake_up_interruptible(&reply->reply_dst->wait); -} - /* * Check for maximum number of messages per individual user. This * should prevent a single user from being able to fill the receiver's @@ -218,62 +106,10 @@ static int kdbus_conn_queue_user_quota(const struct kdbus_conn *conn_src, static void kdbus_conn_work(struct work_struct *work) { - struct kdbus_conn *conn; - struct kdbus_reply *reply, *reply_tmp; - u64 deadline = ~0ULL; - struct timespec64 ts; - u64 now; + struct kdbus_conn *conn = + container_of(work, struct kdbus_conn, work.work); - conn = container_of(work, struct kdbus_conn, work.work); - ktime_get_ts64(&ts); - now = timespec64_to_ns(&ts); - - mutex_lock(&conn->lock); - if (!kdbus_conn_active(conn)) { - mutex_unlock(&conn->lock); - return; - } - - list_for_each_entry_safe(reply, reply_tmp, &conn->reply_list, entry) { - /* - * If the reply block is waiting for synchronous I/O, - * the timeout is handled by wait_event_*_timeout(), - * so we don't have to care for it here. - */ - if (reply->sync && !reply->interrupted) - continue; - - if (reply->deadline_ns > now) { - /* remember next timeout */ - if (deadline > reply->deadline_ns) - deadline = reply->deadline_ns; - - continue; - } - - /* - * A zero deadline means the connection died, was - * cleaned up already and the notification was sent. - * Don't send notifications for reply trackers that were - * left in an interrupted syscall state. - */ - if (reply->deadline_ns != 0 && !reply->interrupted) - kdbus_notify_reply_timeout(conn->ep->bus, - reply->reply_dst->id, - reply->cookie); - - list_del_init(&reply->entry); - kdbus_reply_unref(reply); - } - - /* rearm delayed work with next timeout */ - if (deadline != ~0ULL) - schedule_delayed_work(&conn->work, - nsecs_to_jiffies(deadline - now)); - - mutex_unlock(&conn->lock); - - kdbus_notify_flush(conn->ep->bus); + kdbus_reply_list_scan(conn); } /** @@ -402,42 +238,6 @@ exit_unlock: return ret; } -/** - * kdbus_reply_find() - Find the corresponding reply object - * @replying: The replying connection - * @reply_dst: The connection the reply will be sent to - * (method origin) - * @cookie: The cookie of the requesting message - * - * Lookup a reply object that should be sent as a reply by - * @replying to @reply_dst with the given cookie. - * - * For optimizations, callers should first check 'request_count' of - * @reply_dst to see if the connection has issued any requests - * that are waiting for replies, before calling this function. - * - * Callers must take the @replying lock. - * - * Return: the corresponding reply object or NULL if not found - */ -static struct kdbus_reply * -kdbus_reply_find(struct kdbus_conn *replying, - struct kdbus_conn *reply_dst, - u64 cookie) -{ - struct kdbus_reply *r, *reply = NULL; - - list_for_each_entry(r, &replying->reply_list, entry) { - if (r->reply_dst == reply_dst && - r->cookie == cookie) { - reply = r; - break; - } - } - - return reply; -} - static int kdbus_conn_check_access(struct kdbus_conn *conn_src, const struct cred *conn_src_creds, struct kdbus_conn *conn_dst, diff --git a/reply.c b/reply.c new file mode 100644 index 0000000..ab72868 --- /dev/null +++ b/reply.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include + +#include "bus.h" +#include "connection.h" +#include "endpoint.h" +#include "message.h" +#include "metadata.h" +#include "domain.h" +#include "item.h" +#include "notify.h" +#include "policy.h" +#include "reply.h" +#include "util.h" + +struct kdbus_reply * +kdbus_reply_new(struct kdbus_conn *reply_dst, + const struct kdbus_msg *msg, + struct kdbus_name_entry *name_entry, + bool sync) +{ + struct kdbus_reply *r; + int ret = 0; + + if (atomic_inc_return(&reply_dst->request_count) > + KDBUS_CONN_MAX_REQUESTS_PENDING) { + ret = -EMLINK; + goto exit_dec_request_count; + } + + r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) { + ret = -ENOMEM; + goto exit_dec_request_count; + } + + kref_init(&r->kref); + r->reply_dst = kdbus_conn_ref(reply_dst); + r->cookie = msg->cookie; + r->name_id = name_entry ? name_entry->name_id : 0; + r->deadline_ns = msg->timeout_ns; + + if (sync) { + r->sync = true; + r->waiting = true; + } + +exit_dec_request_count: + if (ret < 0) { + atomic_dec(&reply_dst->request_count); + return ERR_PTR(ret); + } + + return r; +} + +static void __kdbus_reply_free(struct kref *kref) +{ + struct kdbus_reply *reply = + container_of(kref, struct kdbus_reply, kref); + + atomic_dec(&reply->reply_dst->request_count); + kdbus_conn_unref(reply->reply_dst); + kfree(reply); +} + +struct kdbus_reply * +kdbus_reply_ref(struct kdbus_reply *r) +{ + if (r) + kref_get(&r->kref); + return r; +} + +struct kdbus_reply * +kdbus_reply_unref(struct kdbus_reply *r) +{ + if (r) + kref_put(&r->kref, __kdbus_reply_free); + return NULL; +} + +/* + * Remove the synchronous reply object from its connection + * reply_list, and wakeup remote peer (method origin) with the + * appropriate synchronous reply code + */ +void kdbus_sync_reply_wakeup(struct kdbus_reply *reply, int err) +{ + if (WARN_ON(!reply->sync)) + return; + + list_del_init(&reply->entry); + reply->waiting = false; + reply->err = err; + wake_up_interruptible(&reply->reply_dst->wait); +} + +/** + * kdbus_reply_find() - Find the corresponding reply object + * @replying: The replying connection + * @reply_dst: The connection the reply will be sent to + * (method origin) + * @cookie: The cookie of the requesting message + * + * Lookup a reply object that should be sent as a reply by + * @replying to @reply_dst with the given cookie. + * + * For optimizations, callers should first check 'request_count' of + * @reply_dst to see if the connection has issued any requests + * that are waiting for replies, before calling this function. + * + * Callers must take the @replying lock. + * + * Return: the corresponding reply object or NULL if not found + */ +struct kdbus_reply * +kdbus_reply_find(struct kdbus_conn *replying, + struct kdbus_conn *reply_dst, + u64 cookie) +{ + struct kdbus_reply *r, *reply = NULL; + + list_for_each_entry(r, &replying->reply_list, entry) { + if (r->reply_dst == reply_dst && + r->cookie == cookie) { + reply = r; + break; + } + } + + return reply; +} + +void kdbus_reply_list_scan(struct kdbus_conn *conn) +{ + struct kdbus_reply *reply, *reply_tmp; + u64 deadline = ~0ULL; + struct timespec64 ts; + u64 now; + + ktime_get_ts64(&ts); + now = timespec64_to_ns(&ts); + + mutex_lock(&conn->lock); + if (!kdbus_conn_active(conn)) { + mutex_unlock(&conn->lock); + return; + } + + list_for_each_entry_safe(reply, reply_tmp, &conn->reply_list, entry) { + /* + * If the reply block is waiting for synchronous I/O, + * the timeout is handled by wait_event_*_timeout(), + * so we don't have to care for it here. + */ + if (reply->sync && !reply->interrupted) + continue; + + if (reply->deadline_ns > now) { + /* remember next timeout */ + if (deadline > reply->deadline_ns) + deadline = reply->deadline_ns; + + continue; + } + + /* + * A zero deadline means the connection died, was + * cleaned up already and the notification was sent. + * Don't send notifications for reply trackers that were + * left in an interrupted syscall state. + */ + if (reply->deadline_ns != 0 && !reply->interrupted) + kdbus_notify_reply_timeout(conn->ep->bus, + reply->reply_dst->id, + reply->cookie); + + list_del_init(&reply->entry); + kdbus_reply_unref(reply); + } + + /* rearm delayed work with next timeout */ + if (deadline != ~0ULL) + schedule_delayed_work(&conn->work, + nsecs_to_jiffies(deadline - now)); + + mutex_unlock(&conn->lock); + + kdbus_notify_flush(conn->ep->bus); +} diff --git a/reply.h b/reply.h new file mode 100644 index 0000000..5d3f61b --- /dev/null +++ b/reply.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013-2014 Kay Sievers + * Copyright (C) 2013-2014 Greg Kroah-Hartman + * Copyright (C) 2013-2014 Daniel Mack + * Copyright (C) 2013-2014 David Herrmann + * Copyright (C) 2013-2014 Linux Foundation + * Copyright (C) 2014 Djalal Harouni + * + * kdbus is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. + */ + +#ifndef __KDBUS_REPLY_H +#define __KDBUS_REPLY_H + +/** + * struct kdbus_reply - an entry of kdbus_conn's list of replies + * @kref: Ref-count of this object + * @entry: The entry of the connection's reply_list + * @reply_dst: The connection the reply will be sent to (method origin) + * @queue_entry: The queue entry item that is prepared by the replying + * connection + * @deadline_ns: The deadline of the reply, in nanoseconds + * @cookie: The cookie of the requesting message + * @name_id: ID of the well-known name the original msg was sent to + * @sync: The reply block is waiting for synchronous I/O + * @waiting: The condition to synchronously wait for + * @interrupted: The sync reply was left in an interrupted state + * @err: The error code for the synchronous reply + */ +struct kdbus_reply { + struct kref kref; + struct list_head entry; + struct kdbus_conn *reply_dst; + struct kdbus_queue_entry *queue_entry; + u64 deadline_ns; + u64 cookie; + u64 name_id; + bool sync:1; + bool waiting:1; + bool interrupted:1; + int err; +}; + +struct kdbus_reply *kdbus_reply_new(struct kdbus_conn *reply_dst, + const struct kdbus_msg *msg, + struct kdbus_name_entry *name_entry, + bool sync); + +struct kdbus_reply *kdbus_reply_ref(struct kdbus_reply *r); +struct kdbus_reply *kdbus_reply_unref(struct kdbus_reply *r); + +struct kdbus_reply *kdbus_reply_find(struct kdbus_conn *replying, + struct kdbus_conn *reply_dst, + u64 cookie); + +void kdbus_sync_reply_wakeup(struct kdbus_reply *reply, int err); +void kdbus_reply_list_scan(struct kdbus_conn *conn); + +#endif /* __KDBUS_REPLY_H */ -- 2.34.1