From b48dbb998d70b7f48c2ec0a15c3cf47136808e4e Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Sun, 12 Jun 2022 13:42:32 +0900 Subject: 9p fid refcount: add p9_fid_get/put wrappers I was recently reminded that it is not clear that p9_client_clunk() was actually just decrementing refcount and clunking only when that reaches zero: make it clear through a set of helpers. This will also allow instrumenting refcounting better for debugging next patch Link: https://lkml.kernel.org/r/20220612085330.1451496-5-asmadeus@codewreck.org Reviewed-by: Tyler Hicks Reviewed-by: Christian Schoenebeck Signed-off-by: Dominique Martinet --- include/net/9p/client.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'include') diff --git a/include/net/9p/client.h b/include/net/9p/client.h index ec1d1706f43c..eabb53992350 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -237,6 +237,34 @@ static inline int p9_req_try_get(struct p9_req_t *r) int p9_req_put(struct p9_req_t *r); +/* fid reference counting helpers: + * - fids used for any length of time should always be referenced through + * p9_fid_get(), and released with p9_fid_put() + * - v9fs_fid_lookup() or similar will automatically call get for you + * and also require a put + * - the *_fid_add() helpers will stash the fid in the inode, + * at which point it is the responsibility of evict_inode() + * to call the put + * - the last put will automatically send a clunk to the server + */ +static inline struct p9_fid *p9_fid_get(struct p9_fid *fid) +{ + refcount_inc(&fid->count); + + return fid; +} + +static inline int p9_fid_put(struct p9_fid *fid) +{ + if (!fid || IS_ERR(fid)) + return 0; + + if (!refcount_dec_and_test(&fid->count)) + return 0; + + return p9_client_clunk(fid); +} + void p9_client_cb(struct p9_client *c, struct p9_req_t *req, int status); int p9_parse_header(struct p9_fcall *pdu, int32_t *size, int8_t *type, -- cgit v1.2.3 From 286c171b86ebc693e18b485dde3a3fc470af37bd Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Mon, 13 Jun 2022 08:32:06 +0900 Subject: 9p fid refcount: add a 9p_fid_ref tracepoint This adds a tracepoint event for 9p fid lifecycle tracing: when a fid is created, its reference count increased/decreased, and freed. The new 9p_fid_ref tracepoint should help anyone wishing to debug any fid problem such as missing clunk (destroy) or use-after-free. Link: https://lkml.kernel.org/r/20220612085330.1451496-6-asmadeus@codewreck.org Signed-off-by: Dominique Martinet --- include/net/9p/client.h | 13 +++++++++++++ include/trace/events/9p.h | 48 +++++++++++++++++++++++++++++++++++++++++++++++ net/9p/client.c | 20 +++++++++++++++++++- 3 files changed, 80 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/9p/client.h b/include/net/9p/client.h index eabb53992350..8f629f1df865 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -11,6 +11,7 @@ #include #include +#include /* Number of requests per row */ #define P9_ROW_MAXTAG 255 @@ -237,6 +238,12 @@ static inline int p9_req_try_get(struct p9_req_t *r) int p9_req_put(struct p9_req_t *r); +/* We cannot have the real tracepoints in header files, + * use a wrapper function */ +DECLARE_TRACEPOINT(9p_fid_ref); +void do_trace_9p_fid_get(struct p9_fid *fid); +void do_trace_9p_fid_put(struct p9_fid *fid); + /* fid reference counting helpers: * - fids used for any length of time should always be referenced through * p9_fid_get(), and released with p9_fid_put() @@ -249,6 +256,9 @@ int p9_req_put(struct p9_req_t *r); */ static inline struct p9_fid *p9_fid_get(struct p9_fid *fid) { + if (tracepoint_enabled(9p_fid_ref)) + do_trace_9p_fid_get(fid); + refcount_inc(&fid->count); return fid; @@ -259,6 +269,9 @@ static inline int p9_fid_put(struct p9_fid *fid) if (!fid || IS_ERR(fid)) return 0; + if (tracepoint_enabled(9p_fid_ref)) + do_trace_9p_fid_put(fid); + if (!refcount_dec_and_test(&fid->count)) return 0; diff --git a/include/trace/events/9p.h b/include/trace/events/9p.h index 78c5608a1648..4dfa6d7f83ba 100644 --- a/include/trace/events/9p.h +++ b/include/trace/events/9p.h @@ -77,6 +77,13 @@ EM( P9_TWSTAT, "P9_TWSTAT" ) \ EMe(P9_RWSTAT, "P9_RWSTAT" ) + +#define P9_FID_REFTYPE \ + EM( P9_FID_REF_CREATE, "create " ) \ + EM( P9_FID_REF_GET, "get " ) \ + EM( P9_FID_REF_PUT, "put " ) \ + EMe(P9_FID_REF_DESTROY, "destroy" ) + /* Define EM() to export the enums to userspace via TRACE_DEFINE_ENUM() */ #undef EM #undef EMe @@ -84,6 +91,21 @@ #define EMe(a, b) TRACE_DEFINE_ENUM(a); P9_MSG_T +P9_FID_REFTYPE + +/* And also use EM/EMe to define helper enums -- once */ +#ifndef __9P_DECLARE_TRACE_ENUMS_ONLY_ONCE +#define __9P_DECLARE_TRACE_ENUMS_ONLY_ONCE +#undef EM +#undef EMe +#define EM(a, b) a, +#define EMe(a, b) a + +enum p9_fid_reftype { + P9_FID_REFTYPE +} __mode(byte); + +#endif /* * Now redefine the EM() and EMe() macros to map the enums to the strings @@ -96,6 +118,8 @@ P9_MSG_T #define show_9p_op(type) \ __print_symbolic(type, P9_MSG_T) +#define show_9p_fid_reftype(type) \ + __print_symbolic(type, P9_FID_REFTYPE) TRACE_EVENT(9p_client_req, TP_PROTO(struct p9_client *clnt, int8_t type, int tag), @@ -168,6 +192,30 @@ TRACE_EVENT(9p_protocol_dump, __entry->tag, 0, __entry->line, 16, __entry->line + 16) ); + +TRACE_EVENT(9p_fid_ref, + TP_PROTO(struct p9_fid *fid, __u8 type), + + TP_ARGS(fid, type), + + TP_STRUCT__entry( + __field( int, fid ) + __field( int, refcount ) + __field( __u8, type ) + ), + + TP_fast_assign( + __entry->fid = fid->fid; + __entry->refcount = refcount_read(&fid->count); + __entry->type = type; + ), + + TP_printk("%s fid %d, refcount %d", + show_9p_fid_reftype(__entry->type), + __entry->fid, __entry->refcount) +); + + #endif /* _TRACE_9P_H */ /* This part must be outside protection */ diff --git a/net/9p/client.c b/net/9p/client.c index f3eb280c7d9d..dfe8beb864fc 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -907,8 +907,10 @@ static struct p9_fid *p9_fid_create(struct p9_client *clnt) GFP_NOWAIT); spin_unlock_irq(&clnt->lock); idr_preload_end(); - if (!ret) + if (!ret) { + trace_9p_fid_ref(fid, P9_FID_REF_CREATE); return fid; + } kfree(fid); return NULL; @@ -920,6 +922,7 @@ static void p9_fid_destroy(struct p9_fid *fid) unsigned long flags; p9_debug(P9_DEBUG_FID, "fid %d\n", fid->fid); + trace_9p_fid_ref(fid, P9_FID_REF_DESTROY); clnt = fid->clnt; spin_lock_irqsave(&clnt->lock, flags); idr_remove(&clnt->fids, fid->fid); @@ -928,6 +931,21 @@ static void p9_fid_destroy(struct p9_fid *fid) kfree(fid); } +/* We also need to export tracepoint symbols for tracepoint_enabled() */ +EXPORT_TRACEPOINT_SYMBOL(9p_fid_ref); + +void do_trace_9p_fid_get(struct p9_fid *fid) +{ + trace_9p_fid_ref(fid, P9_FID_REF_GET); +} +EXPORT_SYMBOL(do_trace_9p_fid_get); + +void do_trace_9p_fid_put(struct p9_fid *fid) +{ + trace_9p_fid_ref(fid, P9_FID_REF_PUT); +} +EXPORT_SYMBOL(do_trace_9p_fid_put); + static int p9_client_version(struct p9_client *c) { int err = 0; -- cgit v1.2.3 From 6cda12864cb0f99810a5809e11e3ee5b102c9a47 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 3 Jul 2022 21:02:49 -0400 Subject: 9p: Drop kref usage An upcoming patch is going to require passing the client through p9_req_put() -> p9_req_free(), but that's awkward with the kref indirection - so this patch switches to using refcount_t directly. Link: https://lkml.kernel.org/r/20220704014243.153050-1-kent.overstreet@gmail.com Signed-off-by: Kent Overstreet Cc: Eric Van Hensbergen Cc: Latchesar Ionkov Signed-off-by: Dominique Martinet --- include/net/9p/client.h | 6 +++--- net/9p/client.c | 19 ++++++++----------- 2 files changed, 11 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 8f629f1df865..ad38325aaef0 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -77,7 +77,7 @@ enum p9_req_status_t { struct p9_req_t { int status; int t_err; - struct kref refcount; + refcount_t refcount; wait_queue_head_t wq; struct p9_fcall tc; struct p9_fcall rc; @@ -228,12 +228,12 @@ struct p9_req_t *p9_tag_lookup(struct p9_client *c, u16 tag); static inline void p9_req_get(struct p9_req_t *r) { - kref_get(&r->refcount); + refcount_inc(&r->refcount); } static inline int p9_req_try_get(struct p9_req_t *r) { - return kref_get_unless_zero(&r->refcount); + return refcount_inc_not_zero(&r->refcount); } int p9_req_put(struct p9_req_t *r); diff --git a/net/9p/client.c b/net/9p/client.c index dfe8beb864fc..3717d3f21a9f 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -305,7 +305,7 @@ p9_tag_alloc(struct p9_client *c, int8_t type, unsigned int max_size) * callback), so p9_client_cb eats the second ref there * as the pointer is duplicated directly by virtqueue_add_sgs() */ - refcount_set(&req->refcount.refcount, 2); + refcount_set(&req->refcount, 2); return req; @@ -370,18 +370,15 @@ static int p9_tag_remove(struct p9_client *c, struct p9_req_t *r) return p9_req_put(r); } -static void p9_req_free(struct kref *ref) -{ - struct p9_req_t *r = container_of(ref, struct p9_req_t, refcount); - - p9_fcall_fini(&r->tc); - p9_fcall_fini(&r->rc); - kmem_cache_free(p9_req_cache, r); -} - int p9_req_put(struct p9_req_t *r) { - return kref_put(&r->refcount, p9_req_free); + if (refcount_dec_and_test(&r->refcount)) { + p9_fcall_fini(&r->tc); + p9_fcall_fini(&r->rc); + kmem_cache_free(p9_req_cache, r); + return 1; + } + return 0; } EXPORT_SYMBOL(p9_req_put); -- cgit v1.2.3 From 8b11ff098af42b1fa57fc817daadd53c8b244a0c Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 3 Jul 2022 21:08:18 -0400 Subject: 9p: Add client parameter to p9_req_put() This is to aid in adding mempools, in the next patch. Link: https://lkml.kernel.org/r/20220704014243.153050-2-kent.overstreet@gmail.com Signed-off-by: Kent Overstreet Cc: Eric Van Hensbergen Cc: Latchesar Ionkov Signed-off-by: Dominique Martinet --- include/net/9p/client.h | 2 +- net/9p/client.c | 12 ++++++------ net/9p/trans_fd.c | 12 ++++++------ net/9p/trans_rdma.c | 2 +- net/9p/trans_virtio.c | 4 ++-- net/9p/trans_xen.c | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/net/9p/client.h b/include/net/9p/client.h index ad38325aaef0..78ebcf782ce5 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -236,7 +236,7 @@ static inline int p9_req_try_get(struct p9_req_t *r) return refcount_inc_not_zero(&r->refcount); } -int p9_req_put(struct p9_req_t *r); +int p9_req_put(struct p9_client *c, struct p9_req_t *r); /* We cannot have the real tracepoints in header files, * use a wrapper function */ diff --git a/net/9p/client.c b/net/9p/client.c index 3717d3f21a9f..0be19ab1f693 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -341,7 +341,7 @@ again: if (!p9_req_try_get(req)) goto again; if (req->tc.tag != tag) { - p9_req_put(req); + p9_req_put(c, req); goto again; } } @@ -367,10 +367,10 @@ static int p9_tag_remove(struct p9_client *c, struct p9_req_t *r) spin_lock_irqsave(&c->lock, flags); idr_remove(&c->reqs, tag); spin_unlock_irqrestore(&c->lock, flags); - return p9_req_put(r); + return p9_req_put(c, r); } -int p9_req_put(struct p9_req_t *r) +int p9_req_put(struct p9_client *c, struct p9_req_t *r) { if (refcount_dec_and_test(&r->refcount)) { p9_fcall_fini(&r->tc); @@ -423,7 +423,7 @@ void p9_client_cb(struct p9_client *c, struct p9_req_t *req, int status) wake_up(&req->wq); p9_debug(P9_DEBUG_MUX, "wakeup: %d\n", req->tc.tag); - p9_req_put(req); + p9_req_put(c, req); } EXPORT_SYMBOL(p9_client_cb); @@ -706,7 +706,7 @@ static struct p9_req_t *p9_client_prepare_req(struct p9_client *c, reterr: p9_tag_remove(c, req); /* We have to put also the 2nd reference as it won't be used */ - p9_req_put(req); + p9_req_put(c, req); return ERR_PTR(err); } @@ -743,7 +743,7 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...) err = c->trans_mod->request(c, req); if (err < 0) { /* write won't happen */ - p9_req_put(req); + p9_req_put(c, req); if (err != -ERESTARTSYS && err != -EFAULT) c->status = Disconnected; goto recalc_sigpending; diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index 8f8f95e39b03..007c3f45fe05 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -378,7 +378,7 @@ static void p9_read_work(struct work_struct *work) m->rc.sdata = NULL; m->rc.offset = 0; m->rc.capacity = 0; - p9_req_put(m->rreq); + p9_req_put(m->client, m->rreq); m->rreq = NULL; } @@ -492,7 +492,7 @@ static void p9_write_work(struct work_struct *work) m->wpos += err; if (m->wpos == m->wsize) { m->wpos = m->wsize = 0; - p9_req_put(m->wreq); + p9_req_put(m->client, m->wreq); m->wreq = NULL; } @@ -695,7 +695,7 @@ static int p9_fd_cancel(struct p9_client *client, struct p9_req_t *req) if (req->status == REQ_STATUS_UNSENT) { list_del(&req->req_list); req->status = REQ_STATUS_FLSHD; - p9_req_put(req); + p9_req_put(client, req); ret = 0; } spin_unlock(&client->lock); @@ -722,7 +722,7 @@ static int p9_fd_cancelled(struct p9_client *client, struct p9_req_t *req) list_del(&req->req_list); req->status = REQ_STATUS_FLSHD; spin_unlock(&client->lock); - p9_req_put(req); + p9_req_put(client, req); return 0; } @@ -883,12 +883,12 @@ static void p9_conn_destroy(struct p9_conn *m) p9_mux_poll_stop(m); cancel_work_sync(&m->rq); if (m->rreq) { - p9_req_put(m->rreq); + p9_req_put(m->client, m->rreq); m->rreq = NULL; } cancel_work_sync(&m->wq); if (m->wreq) { - p9_req_put(m->wreq); + p9_req_put(m->client, m->wreq); m->wreq = NULL; } diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index 88e563826674..d817d3745238 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -350,7 +350,7 @@ send_done(struct ib_cq *cq, struct ib_wc *wc) c->busa, c->req->tc.size, DMA_TO_DEVICE); up(&rdma->sq_sem); - p9_req_put(c->req); + p9_req_put(client, c->req); kfree(c); } diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index b24a4fb0f0a2..147972bf2e79 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -199,7 +199,7 @@ static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req) /* Reply won't come, so drop req ref */ static int p9_virtio_cancelled(struct p9_client *client, struct p9_req_t *req) { - p9_req_put(req); + p9_req_put(client, req); return 0; } @@ -523,7 +523,7 @@ err_out: kvfree(out_pages); if (!kicked) { /* reply won't come */ - p9_req_put(req); + p9_req_put(client, req); } return err; } diff --git a/net/9p/trans_xen.c b/net/9p/trans_xen.c index 833cd3792c51..227f89cc7237 100644 --- a/net/9p/trans_xen.c +++ b/net/9p/trans_xen.c @@ -163,7 +163,7 @@ again: ring->intf->out_prod = prod; spin_unlock_irqrestore(&ring->lock, flags); notify_remote_via_irq(ring->irq); - p9_req_put(p9_req); + p9_req_put(client, p9_req); return 0; } -- cgit v1.2.3