xref: /linux/net/rxrpc/txbuf.c (revision a35d00d5512accd337510fa4de756b743d331a87)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* RxRPC Tx data buffering.
3  *
4  * Copyright (C) 2022 Red Hat, Inc. All Rights Reserved.
5  * Written by David Howells (dhowells@redhat.com)
6  */
7 
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9 
10 #include <linux/slab.h>
11 #include "ar-internal.h"
12 
13 static atomic_t rxrpc_txbuf_debug_ids;
14 atomic_t rxrpc_nr_txbuf;
15 
16 /*
17  * Allocate and partially initialise a data transmission buffer.
18  */
19 struct rxrpc_txbuf *rxrpc_alloc_data_txbuf(struct rxrpc_call *call, size_t data_size,
20 					   size_t data_align, gfp_t gfp)
21 {
22 	struct rxrpc_wire_header *whdr;
23 	struct rxrpc_txbuf *txb;
24 	size_t total, hoff;
25 	void *buf;
26 
27 	txb = kzalloc(sizeof(*txb), gfp);
28 	if (!txb)
29 		return NULL;
30 
31 	hoff = round_up(sizeof(*whdr), data_align) - sizeof(*whdr);
32 	total = hoff + sizeof(*whdr) + data_size;
33 
34 	data_align = umax(data_align, L1_CACHE_BYTES);
35 	mutex_lock(&call->conn->tx_data_alloc_lock);
36 	buf = page_frag_alloc_align(&call->conn->tx_data_alloc, total, gfp,
37 				    data_align);
38 	mutex_unlock(&call->conn->tx_data_alloc_lock);
39 	if (!buf) {
40 		kfree(txb);
41 		return NULL;
42 	}
43 
44 	whdr = buf + hoff;
45 
46 	refcount_set(&txb->ref, 1);
47 	txb->call_debug_id	= call->debug_id;
48 	txb->debug_id		= atomic_inc_return(&rxrpc_txbuf_debug_ids);
49 	txb->alloc_size		= data_size;
50 	txb->space		= data_size;
51 	txb->offset		= sizeof(*whdr);
52 	txb->flags		= call->conn->out_clientflag;
53 	txb->seq		= call->send_top + 1;
54 	txb->nr_kvec		= 1;
55 	txb->kvec[0].iov_base	= whdr;
56 	txb->kvec[0].iov_len	= sizeof(*whdr);
57 
58 	whdr->epoch		= htonl(call->conn->proto.epoch);
59 	whdr->cid		= htonl(call->cid);
60 	whdr->callNumber	= htonl(call->call_id);
61 	whdr->seq		= htonl(txb->seq);
62 	whdr->type		= RXRPC_PACKET_TYPE_DATA;
63 	whdr->flags		= 0;
64 	whdr->userStatus	= 0;
65 	whdr->securityIndex	= call->security_ix;
66 	whdr->_rsvd		= 0;
67 	whdr->serviceId		= htons(call->dest_srx.srx_service);
68 
69 	trace_rxrpc_txbuf(txb->debug_id, txb->call_debug_id, txb->seq, 1,
70 			  rxrpc_txbuf_alloc_data);
71 
72 	atomic_inc(&rxrpc_nr_txbuf);
73 	return txb;
74 }
75 
76 void rxrpc_get_txbuf(struct rxrpc_txbuf *txb, enum rxrpc_txbuf_trace what)
77 {
78 	int r;
79 
80 	__refcount_inc(&txb->ref, &r);
81 	trace_rxrpc_txbuf(txb->debug_id, txb->call_debug_id, txb->seq, r + 1, what);
82 }
83 
84 void rxrpc_see_txbuf(struct rxrpc_txbuf *txb, enum rxrpc_txbuf_trace what)
85 {
86 	int r = refcount_read(&txb->ref);
87 
88 	trace_rxrpc_txbuf(txb->debug_id, txb->call_debug_id, txb->seq, r, what);
89 }
90 
91 static void rxrpc_free_txbuf(struct rxrpc_txbuf *txb)
92 {
93 	int i;
94 
95 	trace_rxrpc_txbuf(txb->debug_id, txb->call_debug_id, txb->seq, 0,
96 			  rxrpc_txbuf_free);
97 	for (i = 0; i < txb->nr_kvec; i++)
98 		if (txb->kvec[i].iov_base &&
99 		    !is_zero_pfn(page_to_pfn(virt_to_page(txb->kvec[i].iov_base))))
100 			page_frag_free(txb->kvec[i].iov_base);
101 	kfree(txb);
102 	atomic_dec(&rxrpc_nr_txbuf);
103 }
104 
105 void rxrpc_put_txbuf(struct rxrpc_txbuf *txb, enum rxrpc_txbuf_trace what)
106 {
107 	unsigned int debug_id, call_debug_id;
108 	rxrpc_seq_t seq;
109 	bool dead;
110 	int r;
111 
112 	if (txb) {
113 		debug_id = txb->debug_id;
114 		call_debug_id = txb->call_debug_id;
115 		seq = txb->seq;
116 		dead = __refcount_dec_and_test(&txb->ref, &r);
117 		trace_rxrpc_txbuf(debug_id, call_debug_id, seq, r - 1, what);
118 		if (dead)
119 			rxrpc_free_txbuf(txb);
120 	}
121 }
122