diff mbox series

[v2,8/8] rmnet: Implement cancel

Message ID 20241119171832.1119-8-denkenz@gmail.com (mailing list archive)
State Accepted
Commit a0d8117a04c9a955a11e89e7640b5f98a6bbc3f2
Headers show
Series [v2,1/8] rmnet: Add skeleton | expand

Commit Message

Denis Kenzior Nov. 19, 2024, 5:18 p.m. UTC
Implement cancellation of an ongoing rmnet request.  Any links that
have already been created are deleted by putting another request to
the head of the queue.
---
 src/rmnet.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 58 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/src/rmnet.c b/src/rmnet.c
index b51ebfd18f65..83110c33a980 100644
--- a/src/rmnet.c
+++ b/src/rmnet.c
@@ -32,6 +32,7 @@  struct rmnet_request {
 	void *user_data;
 	rmnet_destroy_func_t destroy;
 	int id;
+	bool canceled;
 	uint32_t netlink_id;
 	uint16_t request_type;
 	uint8_t current;
@@ -54,6 +55,14 @@  static void rmnet_request_free(struct rmnet_request *req)
 	l_free(req);
 }
 
+static bool rmnet_request_id_matches(const void *a, const void *b)
+{
+	const struct rmnet_request *req = a;
+	int id = L_PTR_TO_INT(b);
+
+	return req->id == id;
+}
+
 static struct rmnet_request *__rmnet_del_request_new(unsigned int n_interfaces,
 					const struct rmnet_ifinfo *interfaces)
 {
@@ -199,7 +208,7 @@  static void rmnet_new_link_cb(int error, uint16_t type, const void *data,
 	if (!error)
 		req->current += 1;
 
-	if (error) {
+	if (error || req->canceled) {
 		__rmnet_cancel_request();
 		req->n_interfaces = 0;
 	} else {
@@ -273,6 +282,7 @@  int rmnet_get_interfaces(uint32_t parent_ifindex, unsigned int n_interfaces,
 	req->user_data = user_data;
 	req->destroy = destroy;
 	req->id = next_request_id++;
+	req->canceled = false;
 	req->request_type = RTM_NEWLINK;
 	req->netlink_id = 0;
 	req->current = 0;
@@ -309,7 +319,53 @@  int rmnet_del_interfaces(unsigned int n_interfaces,
 
 int rmnet_cancel(int id)
 {
-	return -ENOTSUP;
+	struct rmnet_request *req;
+
+	req = l_queue_peek_head(request_q);
+	if (!req)
+		return -ENOENT;
+
+	/* Simple Case: Request not yet started (not queue head) */
+	if (req->id != id) {
+		req = l_queue_remove_if(request_q, rmnet_request_id_matches,
+						L_INT_TO_PTR(id));
+		if (!req)
+			return -ENOENT;
+
+		DBG("Removing non-head of queue request %d", id);
+		rmnet_request_free(req);
+		return 0;
+	}
+
+	/* Harder Case: In progress, but the next request not in flight */
+	if (!l_netlink_request_sent(rtnl, req->netlink_id)) {
+		DBG("Removing in-progress request (not in flight) %d", id);
+		req = __rmnet_cancel_request();
+		l_netlink_cancel(rtnl, req->netlink_id);
+		rmnet_request_free(req);
+
+		if (l_queue_length(request_q))
+			rmnet_start_next_request();
+
+		return 0;
+	}
+
+	/*
+	 * Hardest Case: In progress, next request in flight
+	 * We have to wait until the next callback since the ifindex won't be
+	 * known until then.
+	 */
+	if (req->destroy)
+		req->destroy(req->user_data);
+
+	req->new_cb = NULL;
+	req->destroy = NULL;
+	req->user_data = NULL;
+
+	DBG("Setting canceled on in-progress request %d", id);
+	req->canceled = true;
+
+	return 0;
 }
 
 static int rmnet_parse_info_data(struct l_netlink_attr *linkinfo,