diff options
| author | Tejun Heo <tj@kernel.org> | 2026-10-07 08:01:02 -1000 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2026-10-07 08:01:02 -1000 |
| commit | 92b542db38bf7cb4de583b2ca37febc549db0ce4 (patch) | |
| tree | 30dcfdb338f7fdba597e1ec9314c2006df86f525 /include/trace | |
| parent | fee1265a725725dac0ff7459f912d69558798b36 (diff) | |
| parent | d1a4549a3645e55cc9ade09e360280590b3c9559 (diff) | |
| download | linux-next-92b542db38bf7cb4de583b2ca37febc549db0ce4.tar.gz linux-next-92b542db38bf7cb4de583b2ca37febc549db0ce4.zip | |
Merge branch 'for-7.3-fixes' into for-next
Diffstat (limited to 'include/trace')
| -rw-r--r-- | include/trace/events/btrfs.h | 39 | ||||
| -rw-r--r-- | include/trace/events/cachefiles.h | 203 | ||||
| -rw-r--r-- | include/trace/events/ceph.h | 67 | ||||
| -rw-r--r-- | include/trace/events/damon.h | 8 | ||||
| -rw-r--r-- | include/trace/events/dma.h | 5 | ||||
| -rw-r--r-- | include/trace/events/f2fs.h | 23 | ||||
| -rw-r--r-- | include/trace/events/huge_memory.h | 2 | ||||
| -rw-r--r-- | include/trace/events/icmp.h | 15 | ||||
| -rw-r--r-- | include/trace/events/landlock.h | 1060 | ||||
| -rw-r--r-- | include/trace/events/lock.h | 10 | ||||
| -rw-r--r-- | include/trace/events/memory-failure.h | 6 | ||||
| -rw-r--r-- | include/trace/events/migrate.h | 11 | ||||
| -rw-r--r-- | include/trace/events/mmflags.h | 17 | ||||
| -rw-r--r-- | include/trace/events/mptcp.h | 6 | ||||
| -rw-r--r-- | include/trace/events/netfs.h | 30 | ||||
| -rw-r--r-- | include/trace/events/pagemap.h | 8 | ||||
| -rw-r--r-- | include/trace/events/qcom_geni_i2c.h | 82 | ||||
| -rw-r--r-- | include/trace/events/qcom_geni_se.h | 157 | ||||
| -rw-r--r-- | include/trace/events/qcom_geni_serial.h | 7 | ||||
| -rw-r--r-- | include/trace/events/rcu.h | 7 | ||||
| -rw-r--r-- | include/trace/events/sched_ext.h | 28 | ||||
| -rw-r--r-- | include/trace/events/timer.h | 2 | ||||
| -rw-r--r-- | include/trace/trace_events.h | 23 |
23 files changed, 1581 insertions, 235 deletions
diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 4c5c47c5edb7..6ecfab97c1a9 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -689,6 +689,41 @@ DEFINE_EVENT(btrfs__ordered_extent, btrfs_ordered_extent_lookup_first, TP_ARGS(inode, ordered) ); +/* + * The writepage fixup worker deferred a block because this still-running + * ordered extent covers it. + */ +DEFINE_EVENT(btrfs__ordered_extent, btrfs_writepage_fixup_defer, + + TP_PROTO(const struct btrfs_inode *inode, + const struct btrfs_ordered_extent *ordered), + + TP_ARGS(inode, ordered) +); + +/* The writepage fixup worker reserved space for a block and set delalloc. */ +TRACE_EVENT(btrfs_writepage_fixup_reserve, + + TP_PROTO(const struct btrfs_inode *inode, u64 start, u32 len), + + TP_ARGS(inode, start, len), + + TP_STRUCT__entry_btrfs( + __field( u64, ino ) + __field( u64, start ) + __field( u32, len ) + ), + + TP_fast_assign_btrfs(inode->root->fs_info, + __entry->ino = btrfs_ino(inode); + __entry->start = start; + __entry->len = len; + ), + + TP_printk_btrfs("ino=%llu start=%llu len=%u", + __entry->ino, __entry->start, __entry->len) +); + DEFINE_EVENT(btrfs__ordered_extent, btrfs_ordered_extent_split, TP_PROTO(const struct btrfs_inode *inode, @@ -1578,9 +1613,9 @@ TRACE_EVENT(btrfs_sync_log_enter, __entry->log_transid_committed = data_race(root->log_transid_committed); __entry->log_committing = - atomic_read(&root->log_commit[ctx->log_transid % 2]); + data_race(root->log_commit[ctx->log_transid % 2]); __entry->log_committing_prev = - atomic_read(&root->log_commit[(ctx->log_transid + 1) % 2]); + data_race(root->log_commit[(ctx->log_transid + 1) % 2]); __entry->log_writers = atomic_read(&root->log_writers); ), diff --git a/include/trace/events/cachefiles.h b/include/trace/events/cachefiles.h index 6e3b1424eea4..e3101410e8b2 100644 --- a/include/trace/events/cachefiles.h +++ b/include/trace/events/cachefiles.h @@ -31,10 +31,6 @@ enum cachefiles_obj_ref_trace { cachefiles_obj_see_lookup_failed, cachefiles_obj_see_withdraw_cookie, cachefiles_obj_see_withdrawal, - cachefiles_obj_get_ondemand_fd, - cachefiles_obj_put_ondemand_fd, - cachefiles_obj_get_read_req, - cachefiles_obj_put_read_req, }; enum fscache_why_object_killed { @@ -129,11 +125,7 @@ enum cachefiles_error_trace { EM(cachefiles_obj_see_lookup_cookie, "SEE lookup_cookie") \ EM(cachefiles_obj_see_lookup_failed, "SEE lookup_failed") \ EM(cachefiles_obj_see_withdraw_cookie, "SEE withdraw_cookie") \ - EM(cachefiles_obj_see_withdrawal, "SEE withdrawal") \ - EM(cachefiles_obj_get_ondemand_fd, "GET ondemand_fd") \ - EM(cachefiles_obj_put_ondemand_fd, "PUT ondemand_fd") \ - EM(cachefiles_obj_get_read_req, "GET read_req") \ - E_(cachefiles_obj_put_read_req, "PUT read_req") + E_(cachefiles_obj_see_withdrawal, "SEE withdrawal") #define cachefiles_coherency_traces \ EM(cachefiles_coherency_check_aux, "BAD aux ") \ @@ -380,7 +372,7 @@ TRACE_EVENT(cachefiles_rename, TRACE_EVENT(cachefiles_coherency, TP_PROTO(struct cachefiles_object *obj, ino_t ino, - u64 disk_aux, + const void *disk_aux, enum cachefiles_content content, enum cachefiles_coherency_trace why), @@ -397,12 +389,27 @@ TRACE_EVENT(cachefiles_coherency, ), TP_fast_assign( + union { + __be16 s[4]; + __be64 ll; + } x; + __entry->obj = obj->debug_id; __entry->why = why; __entry->content = content; __entry->ino = ino; __entry->aux = be64_to_cpup((__be64 *)obj->cookie->inline_aux); - __entry->disk_aux = disk_aux; + + /* cachefiles_xattr::data is 2-byte aligned but not 8-byte aligned. */ + if (disk_aux) { + x.s[0] = ((__be16 *)disk_aux)[0]; + x.s[1] = ((__be16 *)disk_aux)[1]; + x.s[2] = ((__be16 *)disk_aux)[2]; + x.s[3] = ((__be16 *)disk_aux)[3]; + __entry->disk_aux = be64_to_cpu(x.ll); + } else { + __entry->disk_aux = 0; + } ), TP_printk("o=%08x %s B=%llx c=%u aux=%llx dsk=%llx", @@ -687,180 +694,6 @@ TRACE_EVENT(cachefiles_io_error, __entry->error) ); -TRACE_EVENT(cachefiles_ondemand_open, - TP_PROTO(struct cachefiles_object *obj, struct cachefiles_msg *msg, - struct cachefiles_open *load), - - TP_ARGS(obj, msg, load), - - TP_STRUCT__entry( - __field(unsigned int, obj) - __field(unsigned int, msg_id) - __field(unsigned int, object_id) - __field(unsigned int, fd) - __field(unsigned int, flags) - ), - - TP_fast_assign( - __entry->obj = obj ? obj->debug_id : 0; - __entry->msg_id = msg->msg_id; - __entry->object_id = msg->object_id; - __entry->fd = load->fd; - __entry->flags = load->flags; - ), - - TP_printk("o=%08x mid=%x oid=%x fd=%d f=%x", - __entry->obj, - __entry->msg_id, - __entry->object_id, - __entry->fd, - __entry->flags) - ); - -TRACE_EVENT(cachefiles_ondemand_copen, - TP_PROTO(struct cachefiles_object *obj, unsigned int msg_id, - long len), - - TP_ARGS(obj, msg_id, len), - - TP_STRUCT__entry( - __field(unsigned int, obj) - __field(unsigned int, msg_id) - __field(long, len) - ), - - TP_fast_assign( - __entry->obj = obj ? obj->debug_id : 0; - __entry->msg_id = msg_id; - __entry->len = len; - ), - - TP_printk("o=%08x mid=%x l=%lx", - __entry->obj, - __entry->msg_id, - __entry->len) - ); - -TRACE_EVENT(cachefiles_ondemand_close, - TP_PROTO(struct cachefiles_object *obj, struct cachefiles_msg *msg), - - TP_ARGS(obj, msg), - - TP_STRUCT__entry( - __field(unsigned int, obj) - __field(unsigned int, msg_id) - __field(unsigned int, object_id) - ), - - TP_fast_assign( - __entry->obj = obj ? obj->debug_id : 0; - __entry->msg_id = msg->msg_id; - __entry->object_id = msg->object_id; - ), - - TP_printk("o=%08x mid=%x oid=%x", - __entry->obj, - __entry->msg_id, - __entry->object_id) - ); - -TRACE_EVENT(cachefiles_ondemand_read, - TP_PROTO(struct cachefiles_object *obj, struct cachefiles_msg *msg, - struct cachefiles_read *load), - - TP_ARGS(obj, msg, load), - - TP_STRUCT__entry( - __field(unsigned int, obj) - __field(unsigned int, msg_id) - __field(unsigned int, object_id) - __field(loff_t, start) - __field(size_t, len) - ), - - TP_fast_assign( - __entry->obj = obj ? obj->debug_id : 0; - __entry->msg_id = msg->msg_id; - __entry->object_id = msg->object_id; - __entry->start = load->off; - __entry->len = load->len; - ), - - TP_printk("o=%08x mid=%x oid=%x s=%llx l=%zx", - __entry->obj, - __entry->msg_id, - __entry->object_id, - __entry->start, - __entry->len) - ); - -TRACE_EVENT(cachefiles_ondemand_cread, - TP_PROTO(struct cachefiles_object *obj, unsigned int msg_id), - - TP_ARGS(obj, msg_id), - - TP_STRUCT__entry( - __field(unsigned int, obj) - __field(unsigned int, msg_id) - ), - - TP_fast_assign( - __entry->obj = obj ? obj->debug_id : 0; - __entry->msg_id = msg_id; - ), - - TP_printk("o=%08x mid=%x", - __entry->obj, - __entry->msg_id) - ); - -TRACE_EVENT(cachefiles_ondemand_fd_write, - TP_PROTO(struct cachefiles_object *obj, struct inode *backer, - loff_t start, size_t len), - - TP_ARGS(obj, backer, start, len), - - TP_STRUCT__entry( - __field(unsigned int, obj) - __field(unsigned int, backer) - __field(loff_t, start) - __field(size_t, len) - ), - - TP_fast_assign( - __entry->obj = obj ? obj->debug_id : 0; - __entry->backer = backer->i_ino; - __entry->start = start; - __entry->len = len; - ), - - TP_printk("o=%08x iB=%x s=%llx l=%zx", - __entry->obj, - __entry->backer, - __entry->start, - __entry->len) - ); - -TRACE_EVENT(cachefiles_ondemand_fd_release, - TP_PROTO(struct cachefiles_object *obj, int object_id), - - TP_ARGS(obj, object_id), - - TP_STRUCT__entry( - __field(unsigned int, obj) - __field(unsigned int, object_id) - ), - - TP_fast_assign( - __entry->obj = obj ? obj->debug_id : 0; - __entry->object_id = object_id; - ), - - TP_printk("o=%08x oid=%x", - __entry->obj, - __entry->object_id) - ); - #endif /* _TRACE_CACHEFILES_H */ /* This part must be outside protection */ diff --git a/include/trace/events/ceph.h b/include/trace/events/ceph.h index 08cb0659fbfc..1b990632f62b 100644 --- a/include/trace/events/ceph.h +++ b/include/trace/events/ceph.h @@ -226,6 +226,73 @@ TRACE_EVENT(ceph_handle_caps, __entry->mseq) ); +/* + * Client reset tracepoints - identify the client by its monitor- + * assigned global_id so traces remain meaningful when kernel pointer + * hashing is enabled. + */ +TRACE_EVENT(ceph_client_reset_schedule, + TP_PROTO(const struct ceph_mds_client *mdsc, const char *reason), + TP_ARGS(mdsc, reason), + TP_STRUCT__entry( + __field(u64, client_id) + __string(reason, reason ? reason : "") + ), + TP_fast_assign( + __entry->client_id = mdsc->fsc->client->monc.auth ? + mdsc->fsc->client->monc.auth->global_id : 0; + __assign_str(reason); + ), + TP_printk("client_id=%llu reason=%s", + __entry->client_id, __get_str(reason)) +); + +TRACE_EVENT(ceph_client_reset_complete, + TP_PROTO(const struct ceph_mds_client *mdsc, int ret), + TP_ARGS(mdsc, ret), + TP_STRUCT__entry( + __field(u64, client_id) + __field(int, ret) + ), + TP_fast_assign( + __entry->client_id = mdsc->fsc->client->monc.auth ? + mdsc->fsc->client->monc.auth->global_id : 0; + __entry->ret = ret; + ), + TP_printk("client_id=%llu ret=%d", __entry->client_id, __entry->ret) +); + +TRACE_EVENT(ceph_client_reset_blocked, + TP_PROTO(const struct ceph_mds_client *mdsc, int blocked_count), + TP_ARGS(mdsc, blocked_count), + TP_STRUCT__entry( + __field(u64, client_id) + __field(int, blocked_count) + ), + TP_fast_assign( + __entry->client_id = mdsc->fsc->client->monc.auth ? + mdsc->fsc->client->monc.auth->global_id : 0; + __entry->blocked_count = blocked_count; + ), + TP_printk("client_id=%llu blocked_count=%d", __entry->client_id, + __entry->blocked_count) +); + +TRACE_EVENT(ceph_client_reset_unblocked, + TP_PROTO(const struct ceph_mds_client *mdsc, int ret), + TP_ARGS(mdsc, ret), + TP_STRUCT__entry( + __field(u64, client_id) + __field(int, ret) + ), + TP_fast_assign( + __entry->client_id = mdsc->fsc->client->monc.auth ? + mdsc->fsc->client->monc.auth->global_id : 0; + __entry->ret = ret; + ), + TP_printk("client_id=%llu ret=%d", __entry->client_id, __entry->ret) +); + #undef EM #undef E_ #endif /* _TRACE_CEPH_H */ diff --git a/include/trace/events/damon.h b/include/trace/events/damon.h index 78388538acf4..8851727ae162 100644 --- a/include/trace/events/damon.h +++ b/include/trace/events/damon.h @@ -78,9 +78,11 @@ TRACE_EVENT_CONDITION(damos_before_apply, TP_PROTO(unsigned int context_idx, unsigned int scheme_idx, unsigned int target_idx, struct damon_region *r, - unsigned int nr_regions, bool do_trace), + unsigned int nr_accesses, unsigned int nr_regions, + bool do_trace), - TP_ARGS(context_idx, scheme_idx, target_idx, r, nr_regions, do_trace), + TP_ARGS(context_idx, scheme_idx, target_idx, r, nr_accesses, + nr_regions, do_trace), TP_CONDITION(do_trace), @@ -101,7 +103,7 @@ TRACE_EVENT_CONDITION(damos_before_apply, __entry->target_idx = target_idx; __entry->start = r->ar.start; __entry->end = r->ar.end; - __entry->nr_accesses = r->nr_accesses_bp / 10000; + __entry->nr_accesses = nr_accesses; __entry->age = r->age; __entry->nr_regions = nr_regions; ), diff --git a/include/trace/events/dma.h b/include/trace/events/dma.h index 31c9ddf72c9d..b06d8f99922d 100644 --- a/include/trace/events/dma.h +++ b/include/trace/events/dma.h @@ -35,7 +35,8 @@ TRACE_DEFINE_ENUM(DMA_NONE); { DMA_ATTR_MMIO, "MMIO" }, \ { DMA_ATTR_DEBUGGING_IGNORE_CACHELINES, "CACHELINES_OVERLAP" }, \ { DMA_ATTR_REQUIRE_COHERENT, "REQUIRE_COHERENT" }, \ - { DMA_ATTR_CC_SHARED, "CC_SHARED" }) + { DMA_ATTR_CC_SHARED, "CC_SHARED" }, \ + { __DMA_ATTR_ALLOC_CC_SHARED, "ALLOC_CC_SHARED" }) DECLARE_EVENT_CLASS(dma_map, TP_PROTO(struct device *dev, phys_addr_t phys_addr, dma_addr_t dma_addr, @@ -133,7 +134,7 @@ DECLARE_EVENT_CLASS(dma_alloc_class, TP_fast_assign( __assign_str(device); __entry->virt_addr = virt_addr; - __entry->dma_addr = dma_addr; + __entry->dma_addr = virt_addr ? dma_addr : 0; __entry->size = size; __entry->flags = flags; __entry->dir = dir; diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 270c1a2c24c4..d53be932df01 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -191,7 +191,8 @@ TRACE_DEFINE_ENUM(CP_PHASE_FINISH_CHECKPOINT); { LOCK_NAME_NODE_WRITE, "node_write" }, \ { LOCK_NAME_GC_LOCK, "gc_lock" }, \ { LOCK_NAME_CP_GLOBAL, "cp_global" }, \ - { LOCK_NAME_IO_RWSEM, "io_rwsem" }) + { LOCK_NAME_IO_RWSEM, "io_rwsem" }, \ + { LOCK_NAME_NAT_TREE_LOCK, "nat_tree_lock" }) struct f2fs_sb_info; struct f2fs_io_info; @@ -2114,6 +2115,14 @@ DEFINE_EVENT(f2fs_zip_end, f2fs_decompress_pages_end, ); #ifdef CONFIG_F2FS_IOSTAT +/* + * Number of read folio order buckets emitted by the f2fs_iostat tracepoint. + * TP_printk() cannot loop, so the field count is fixed here and must be >= + * the largest possible NR_PAGE_ORDERS (14 on arm64 with 64K pages). The + * BUILD_BUG_ON() in f2fs_update_read_folio_count() enforces this. + */ +#define F2FS_IOSTAT_RD_FOLIO_ORDERS 16 + TRACE_EVENT(f2fs_iostat, TP_PROTO(struct f2fs_sb_info *sbi, unsigned long long *iostat, @@ -2151,7 +2160,7 @@ TRACE_EVENT(f2fs_iostat, __field(unsigned long long, fs_mrio) __field(unsigned long long, fs_discard) __field(unsigned long long, fs_reset_zone) - __array(unsigned long long, read_folio_count, 11) + __array(unsigned long long, read_folio_count, F2FS_IOSTAT_RD_FOLIO_ORDERS) ), TP_fast_assign( @@ -2186,7 +2195,8 @@ TRACE_EVENT(f2fs_iostat, __entry->fs_reset_zone = iostat[FS_ZONE_RESET_IO]; memset(__entry->read_folio_count, 0, sizeof(__entry->read_folio_count)); memcpy(__entry->read_folio_count, read_folio_count, - sizeof(unsigned long long) * min_t(int, NR_PAGE_ORDERS, 11)); + sizeof(unsigned long long) * + min_t(int, NR_PAGE_ORDERS, F2FS_IOSTAT_RD_FOLIO_ORDERS)); ), TP_printk("dev = (%d,%d), " @@ -2201,7 +2211,8 @@ TRACE_EVENT(f2fs_iostat, "fs [data=%llu, (gc_data=%llu, cdata=%llu), " "node=%llu, meta=%llu], " "read_folio_count [0=%llu, 1=%llu, 2=%llu, 3=%llu, 4=%llu, " - "5=%llu, 6=%llu, 7=%llu, 8=%llu, 9=%llu, 10=%llu]", + "5=%llu, 6=%llu, 7=%llu, 8=%llu, 9=%llu, 10=%llu, 11=%llu, " + "12=%llu, 13=%llu, 14=%llu, 15=%llu]", show_dev(__entry->dev), __entry->app_wio, __entry->app_dio, __entry->app_bio, __entry->app_mio, __entry->app_bcdio, __entry->app_mcdio, __entry->fs_dio, __entry->fs_cdio, @@ -2218,7 +2229,9 @@ TRACE_EVENT(f2fs_iostat, __entry->read_folio_count[4], __entry->read_folio_count[5], __entry->read_folio_count[6], __entry->read_folio_count[7], __entry->read_folio_count[8], __entry->read_folio_count[9], - __entry->read_folio_count[10]) + __entry->read_folio_count[10], __entry->read_folio_count[11], + __entry->read_folio_count[12], __entry->read_folio_count[13], + __entry->read_folio_count[14], __entry->read_folio_count[15]) ); #ifndef __F2FS_IOSTAT_LATENCY_TYPE diff --git a/include/trace/events/huge_memory.h b/include/trace/events/huge_memory.h index 291fae364c62..5a48c5406cce 100644 --- a/include/trace/events/huge_memory.h +++ b/include/trace/events/huge_memory.h @@ -16,7 +16,7 @@ EM( SCAN_EXCEED_SWAP_PTE, "exceed_swap_pte") \ EM( SCAN_EXCEED_SHARED_PTE, "exceed_shared_pte") \ EM( SCAN_PTE_NON_PRESENT, "pte_non_present") \ - EM( SCAN_PTE_UFFD_WP, "pte_uffd_wp") \ + EM( SCAN_PTE_UFFD, "pte_uffd_wp") \ EM( SCAN_PTE_MAPPED_HUGEPAGE, "pte_mapped_hugepage") \ EM( SCAN_LACK_REFERENCED_PAGE, "lack_referenced_page") \ EM( SCAN_PAGE_NULL, "page_null") \ diff --git a/include/trace/events/icmp.h b/include/trace/events/icmp.h index 31559796949a..6937b778ae54 100644 --- a/include/trace/events/icmp.h +++ b/include/trace/events/icmp.h @@ -27,24 +27,27 @@ TRACE_EVENT(icmp_send, TP_fast_assign( struct iphdr *iph = ip_hdr(skb); - struct udphdr *uh = udp_hdr(skb); - int proto_4 = iph->protocol; + struct udphdr _uh, *uh = NULL; __be32 *p32; __entry->skbaddr = skb; __entry->type = type; __entry->code = code; - if (proto_4 != IPPROTO_UDP || (u8 *)uh < skb->head || - (u8 *)uh + sizeof(struct udphdr) - > skb_tail_pointer(skb)) { + if (iph->protocol == IPPROTO_UDP) + uh = skb_header_pointer(skb, + skb_network_offset(skb) + + (iph->ihl << 2), + sizeof(_uh), &_uh); + + if (!uh) { __entry->sport = 0; __entry->dport = 0; __entry->ulen = 0; } else { __entry->sport = ntohs(uh->source); __entry->dport = ntohs(uh->dest); - __entry->ulen = ntohs(uh->len); + __entry->ulen = udp_get_len_short(uh); } p32 = (__be32 *) __entry->saddr; diff --git a/include/trace/events/landlock.h b/include/trace/events/landlock.h new file mode 100644 index 000000000000..3a43638c9bc2 --- /dev/null +++ b/include/trace/events/landlock.h @@ -0,0 +1,1060 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright © 2025 Microsoft Corporation + * Copyright © 2026 Cloudflare, Inc. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM landlock + +#if !defined(_TRACE_LANDLOCK_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_LANDLOCK_H + +#include <linux/in.h> +#include <linux/in6.h> +#include <linux/landlock.h> +#include <linux/socket.h> +#include <linux/string.h> +#include <linux/string_helpers.h> +#include <linux/tracepoint.h> +#include <linux/trace_seq.h> +#include <net/af_unix.h> + +enum landlock_request_type; +struct dentry; +struct landlock_blockers; +struct landlock_domain; +struct landlock_hierarchy; +struct landlock_rule; +struct landlock_ruleset; +struct path; +struct sock; +struct task_struct; + +static_assert(sizeof(access_mask_t) <= sizeof(u64)); + +TRACE_DEFINE_ENUM(LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY); +TRACE_DEFINE_ENUM(LANDLOCK_REQUEST_FS_ACCESS); +TRACE_DEFINE_ENUM(LANDLOCK_REQUEST_NET_ACCESS); + +#ifdef CREATE_TRACE_POINTS + +/* About 6 KiB, leaving about 2 KiB for sibling helpers and fixed fields. */ +#define TRACE_UNTRUSTED_STR_OUTPUT_SIZE \ + (TRACE_SEQ_BUFFER_SIZE - TRACE_SEQ_BUFFER_SIZE / 4) + +/* + * A raw UTF-8 ellipsis (…) marks truncation and cannot collide with escaped + * input: ESCAPE_NAP renders every non-ASCII input byte in octal. + */ +#define TRACE_TRUNCATION_MARKER "\xe2\x80\xa6" + +/* + * Escapes @len bytes of an untrusted string into the trace sequence @p so it + * cannot inject field separators or control characters into the ftrace text + * output, and can be unambiguously recovered. Called from the TP_printk() of + * the tracepoints that expose paths and process names. @len is passed by the + * caller (rather than derived with strlen()) so a name that is not + * NUL-terminated or carries embedded NUL bytes (an abstract socket name) is + * escaped in full instead of being truncated at the first NUL. + * + * Strings that exceed the output limit retain the largest complete escaped + * prefix followed by the truncation marker. + * + * Return: a pointer into @p's buffer, or NULL if @src is NULL or the fixed + * output reservation is unavailable. + */ +static inline const char * +__trace_print_untrusted_str(struct trace_seq *p, const char *src, size_t len) +{ + const unsigned int escape_flags = ESCAPE_SPACE | ESCAPE_SPECIAL | + ESCAPE_NAP | ESCAPE_APPEND | + ESCAPE_OCTAL; + const size_t marker_len = sizeof(TRACE_TRUNCATION_MARKER) - 1; + size_t buf_size, prefix_len, prefix_size; + int escaped_size; + char *buf; + const char *ret; + + buf_size = seq_buf_get_buf(&p->seq, &buf); + if (!src || buf_size < TRACE_UNTRUSTED_STR_OUTPUT_SIZE) + return NULL; + + ret = trace_seq_buffer_ptr(p); + escaped_size = string_escape_mem(src, len, buf, + TRACE_UNTRUSTED_STR_OUTPUT_SIZE, + escape_flags, " ='\"\\"); + if (likely(escaped_size < TRACE_UNTRUSTED_STR_OUTPUT_SIZE)) { + seq_buf_commit(&p->seq, escaped_size); + trace_seq_putc(p, 0); + return ret; + } + + prefix_len = 0; + prefix_size = 0; + while (prefix_len < len) { + const char *const src_char = src + prefix_len; + int char_size; + + char_size = string_escape_mem(src_char, 1, NULL, 0, + escape_flags, " ='\"\\"); + if (char_size > TRACE_UNTRUSTED_STR_OUTPUT_SIZE - marker_len - + 1 - prefix_size) + break; + prefix_size += char_size; + prefix_len++; + } + + escaped_size = string_escape_mem(src, prefix_len, buf, prefix_size, + escape_flags, " ='\"\\"); + if (WARN_ON_ONCE(escaped_size != prefix_size)) + return NULL; + memcpy(buf + prefix_size, TRACE_TRUNCATION_MARKER, marker_len); + seq_buf_commit(&p->seq, prefix_size + marker_len); + trace_seq_putc(p, 0); + return ret; +} + +/* + * Fills the dense per-domain-layer array layers (one access mask per layer, + * indexed by level - 1) from rule's sparse layer stack, keeping only the + * requested rights (access_request). Layers with no matching rule entry get + * a zero mask. Shared by the check_rule_inode and check_rule_net_port events. + * + * rule->layers is sorted by ascending level, with levels in the domain's + * [1, num_layers] range (see landlock_merge_ruleset()), so every entry maps + * to a slot. A leftover entry would be a malformed rule; the zero-filled + * slots keep the output and the array bounds safe regardless. + */ +static inline void +__trace_landlock_fill_layers(access_mask_t *const layers, + const size_t num_layers, + const struct landlock_rule *const rule, + const access_mask_t access_request) +{ + size_t i = 0; + + for (size_t level = 1; level <= num_layers; level++) { + access_mask_t grants = 0; + + if (i < rule->num_layers && level == rule->layers[i].level) { + grants = rule->layers[i].access & access_request; + i++; + } + layers[level - 1] = grants; + } + + /* A leftover entry means an out-of-range or unsorted rule level. */ + WARN_ON_ONCE(i < rule->num_layers); +} + +/* + * Renders the dense per-domain-layer access array as symbolic flag names for + * the grants field: layers wrapped in "{}", flags within a layer joined by + * "|", layers separated by ",", an empty layer rendered as nothing. + * Open-codes the flag walk because trace_print_flags_seq() NUL-terminates per + * call and so cannot be chained into a single field. The shared names table + * covers every access right, so masked bits are always named. Returns the + * trace_seq position like __print_flags(). + */ +static inline const char *__trace_landlock_print_layers( + struct trace_seq *p, const access_mask_t *const layers, + const size_t num_layers, const struct trace_print_flags *const names, + const size_t names_size) +{ + const char *const ret = trace_seq_buffer_ptr(p); + + trace_seq_putc(p, '{'); + for (size_t i = 0; i < num_layers; i++) { + access_mask_t mask = layers[i]; + bool first = true; + + if (i) + trace_seq_putc(p, ','); + for (size_t j = 0; mask && j < names_size; j++) { + if ((mask & names[j].mask) != names[j].mask) + continue; + if (!first) + trace_seq_putc(p, '|'); + trace_seq_puts(p, names[j].name); + mask &= ~names[j].mask; + first = false; + } + } + trace_seq_putc(p, '}'); + trace_seq_putc(p, 0); + return ret; +} + +#endif /* CREATE_TRACE_POINTS */ + +/* clang-format off */ + +/* Maps a shared _LANDLOCK_*_NAMES entry to a __print_flags() pair. */ +#define _LANDLOCK_NAME_ENTRY(mask, name) { mask, name } + +#define _LANDLOCK_FS_BLOCKER_TYPE_NAMES \ + { LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY, "change_topology" } + +/** + * DOC: Landlock trace events + * + * These guarantees and constraints hold for every Landlock tracepoint. + * A new tracepoint must uphold them, and an eBPF consumer can rely on + * them. + * + * Decision context + * ~~~~~~~~~~~~~~~~ + * + * A denial event identifies the domain whose policy denied the request, the + * Landlock operation and policy object that were checked, and the blocker or + * domain relationship responsible for the denial. When tracing starts with + * sandbox construction, ruleset and domain events provide the policy history + * needed to interpret these identifiers. The denying domain is the subject + * that enforced the policy, not necessarily current. Generic tracepoints can + * provide additional operational context. + * + * Lifecycle consistency + * ~~~~~~~~~~~~~~~~~~~~~~ + * + * Lifecycle emission is balanced: a creation event always has a matching + * deallocation event and vice versa. A consumer that observes an object's + * complete lifetime can model it from this pair; one that attaches late or + * loses exported records must reconcile incomplete state. A creation event + * fires while the object is still private to the calling thread + * (landlock_create_ruleset fires before the ruleset's file descriptor is + * installed, so it cannot race a concurrent :manpage:`close(2)`); if fd + * installation later fails and the ruleset is freed, free_ruleset still + * fires, keeping the pair balanced. The domain pair (create_domain and + * free_domain) is balanced the same way: create_domain fires when the + * domain is created (under the ruleset lock, before thread-sync), and + * free_domain fires when it is freed. A rare thread-sync failure aborts + * the just-created domain, which then emits both events (its creation, then + * an immediate free). Denial events fire only for denials that actually + * happen. + * + * Pointer access + * ~~~~~~~~~~~~~~ + * + * All pointer arguments in TP_PROTO are guaranteed non-NULL by the + * caller, but pointers reached through them may still be NULL (e.g., + * hierarchy->parent at a root domain) and must be checked. eBPF programs + * read these pointers via BTF for richer introspection than the + * TP_STRUCT__entry fields, which serve TP_printk display only. + * + * Mutable object pointers are passed while the caller holds the object's + * lock, so TP_fast_assign and a BTF reader see the exact object the event + * reports, a snapshot no concurrent writer can change: add_rule holds the + * modified ruleset's lock, and create_domain holds the ruleset lock across + * the emission (before the thread-sync wait) so the inspected ruleset is + * the one merged into the domain. Objects immutable at the emission site + * (a domain after creation, a hierarchy at its last reference) need no + * lock. A few values that no held lock protects are a best-effort + * lockless snapshot instead: a task's comm, and the deny_access_net struct + * sock (whose network hook holds no socket lock), matching how the sched + * and signal trace events sample comm. + * + * Field encoding + * ~~~~~~~~~~~~~~ + * + * Fields that mirror the Landlock UAPI preserve their widths and endianness + * (e.g. network ports are u64 in host endianness, like + * landlock_net_port_attr.port). Per-event details, such as where a value + * is byte-swapped, live in the field's own kdoc. + * + * Rule-check fields + * ~~~~~~~~~~~~~~~~~ + * + * The check_rule events fire during an access check, once per matching + * rule, before the final allow-or-deny verdict. They share domain (the + * enforcing domain being evaluated), access_request (the access mask being + * checked), and rule (the matching rule, with per-layer access masks). + * + * Denial fields + * ~~~~~~~~~~~~~ + * + * Every denial event shares three fields. domain is the ID of the + * innermost domain that blocked the access. same_exec tells whether the + * current task is the same executable that entered that domain. logged is + * the domain's audit-logging decision for this denial (its log_status is + * enabled and the per-execution flag selected by same_exec is set); a + * stateless ftrace filter can select the denials the domain submits to + * audit with logged==1, without reconstructing it from the per-execution + * log flags. Denial events order their fields as domain, same_exec, + * logged, then blockers (deny_access events only), then the type-specific + * object fields, then any variable-length field. + * + * Relational referents + * ~~~~~~~~~~~~~~~~~~~~~ + * + * A scope or ptrace verdict compares two domains, so the other party's + * domain is part of the decision context. It is exposed as a scalar + * domain ID (0 when that party is unsandboxed): target_domain (signal), + * peer_domain (abstract unix socket), tracee_domain (ptrace). With both + * IDs in the stream, a consumer that tracked domain creation can relate + * the two parties without kernel-internal state. The ID is a scalar + * snapshot, not a live domain pointer that could dangle: an optional + * relational referent is a scalar (0 sentinel), not a nullable pointer. + * Nonzero IDs are unique within one boot. For ptrace, same_exec instead + * describes the tracer, even for + * PTRACE_TRACEME, and may differ from the current task. + * + * Blocker fields + * ~~~~~~~~~~~~~~ + * + * The filesystem and network blocker arguments identify the request type + * and carry its final missing access subset when applicable. The type + * determines how to interpret the access value. + */ + +/* + * Prints a per-layer access mask array (the dynamic array @array) as symbolic + * flag names using the shared @flag_names list (a _LANDLOCK_*_NAMES macro). + * Stays outside CREATE_TRACE_POINTS: TP_printk is expanded in the print-output + * pass where that macro is undefined. + */ +#define __print_landlock_layers(array, flag_names...) \ + ({ \ + static const struct trace_print_flags __layer_names[] = { \ + flag_names \ + }; \ + __trace_landlock_print_layers( \ + p, __get_dynamic_array(array), \ + __get_dynamic_array_len(array) / \ + sizeof(access_mask_t), \ + __layer_names, ARRAY_SIZE(__layer_names)); \ + }) + +/** + * landlock_create_ruleset - New ruleset created + * + * @ruleset: Newly created ruleset (never NULL); not yet shared via an fd, + * so no lock is needed. + * + * Emitted by sys_landlock_create_ruleset() while the new ruleset is still + * private to the calling thread, before its file descriptor is installed, + * so it cannot race a concurrent :manpage:`close(2)`. Balanced by a + * matching landlock_free_ruleset event. + */ +TRACE_EVENT(landlock_create_ruleset, + + TP_PROTO(const struct landlock_ruleset *ruleset), + + TP_ARGS(ruleset), + + TP_STRUCT__entry( + __field( u64, ruleset_id ) + __field( u64, ruleset_version ) + __field( access_mask_t, handled_fs ) + __field( access_mask_t, handled_net ) + __field( access_mask_t, scoped ) + ), + + TP_fast_assign( + __entry->ruleset_id = ruleset->id; + __entry->ruleset_version = ruleset->version; + __entry->handled_fs = ruleset->handled_masks.fs; + __entry->handled_net = ruleset->handled_masks.net; + __entry->scoped = ruleset->handled_masks.scope; + ), + + TP_printk("ruleset=%llx.%llu handled_fs=%s handled_net=%s scoped=%s", + __entry->ruleset_id, __entry->ruleset_version, + __print_flags(__entry->handled_fs, "|", _LANDLOCK_ACCESS_FS_NAMES), + __print_flags(__entry->handled_net, "|", _LANDLOCK_ACCESS_NET_NAMES), + __print_flags(__entry->scoped, "|", _LANDLOCK_SCOPE_NAMES)) +); + +/** + * landlock_free_ruleset - Ruleset freed + * + * @ruleset: Ruleset being freed (never NULL); at its last reference, so no + * lock is needed. + * + * Emitted when a ruleset's last reference is dropped (typically when + * the creating process closes the ruleset file descriptor). Fires even + * when file-descriptor installation failed after creation, keeping the + * create/free pair balanced. + */ +TRACE_EVENT(landlock_free_ruleset, + + TP_PROTO(const struct landlock_ruleset *ruleset), + + TP_ARGS(ruleset), + + TP_STRUCT__entry( + __field( u64, ruleset_id ) + __field( u64, ruleset_version ) + ), + + TP_fast_assign( + __entry->ruleset_id = ruleset->id; + __entry->ruleset_version = ruleset->version; + ), + + TP_printk("ruleset=%llx.%llu", + __entry->ruleset_id, __entry->ruleset_version) +); + +/** + * landlock_add_rule_path_beneath - Path-beneath rule added to a ruleset + * + * @ruleset: Source ruleset (never NULL). + * @flags: Complete validated landlock_add_rule_flags value supplied by this + * successful call, not the rule's accumulated quiet state. + * @access_rights: Canonical per-call access mask passed to + * landlock_insert_rule() after normalization, not the raw + * sys_landlock_add_rule() argument or accumulated rule. + * @path: Filesystem path for the rule (never NULL). + * @pathname: Resolved absolute path string (never NULL; error placeholder + * on resolution failure). + * + * Emitted by sys_landlock_add_rule() under the modified ruleset's lock, so + * the reported ruleset is a stable snapshot that no concurrent writer can + * change. + */ +TRACE_EVENT(landlock_add_rule_path_beneath, + + TP_PROTO(const struct landlock_ruleset *ruleset, u32 flags, + u64 access_rights, const struct path *path, + const char *pathname), + + TP_ARGS(ruleset, flags, access_rights, path, pathname), + + TP_STRUCT__entry( + __field( u64, ruleset_id ) + __field( u64, ruleset_version ) + __field( access_mask_t, access_rights ) + __field( dev_t, dev ) + __field( ino_t, ino ) + __string( pathname, pathname ) + ), + + TP_fast_assign( + lockdep_assert_held(&ruleset->lock); + __entry->ruleset_id = ruleset->id; + __entry->ruleset_version = ruleset->version; + __entry->access_rights = access_rights; + __entry->dev = path->dentry->d_sb->s_dev; + /* + * The inode number may not be the user-visible one, + * but it will be the same used by audit. + */ + __entry->ino = d_backing_inode(path->dentry)->i_ino; + __assign_str(pathname); + ), + + TP_printk("ruleset=%llx.%llu access_rights=%s dev=%u:%u ino=%lu path=%s", + __entry->ruleset_id, __entry->ruleset_version, + __print_flags(__entry->access_rights, "|", _LANDLOCK_ACCESS_FS_NAMES), + MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, + __trace_print_untrusted_str(p, __get_str(pathname), + __get_dynamic_array_len(pathname) - 1)) +); + +/** + * landlock_add_rule_net_port - Network-port rule added to a ruleset + * + * @ruleset: Source ruleset (never NULL). + * @flags: Complete validated landlock_add_rule_flags value supplied by this + * successful call, not the rule's accumulated quiet state. + * @access_rights: Canonical per-call access mask passed to + * landlock_insert_rule() after normalization, not the raw + * sys_landlock_add_rule() argument or accumulated rule. + * @port: Network port in host endianness, forwarded directly from + * &landlock_net_port_attr.port. + * + * Emitted by sys_landlock_add_rule() under the modified ruleset's lock, so + * the reported ruleset is a stable snapshot that no concurrent writer can + * change. + */ +TRACE_EVENT(landlock_add_rule_net_port, + + TP_PROTO(const struct landlock_ruleset *ruleset, u32 flags, + u64 access_rights, u64 port), + + TP_ARGS(ruleset, flags, access_rights, port), + + TP_STRUCT__entry( + __field( u64, ruleset_id ) + __field( u64, ruleset_version ) + __field( access_mask_t, access_rights ) + __field( u64, port ) + ), + + TP_fast_assign( + lockdep_assert_held(&ruleset->lock); + __entry->ruleset_id = ruleset->id; + __entry->ruleset_version = ruleset->version; + __entry->access_rights = access_rights; + __entry->port = port; + ), + + TP_printk("ruleset=%llx.%llu access_rights=%s port=%llu", + __entry->ruleset_id, __entry->ruleset_version, + __print_flags(__entry->access_rights, "|", _LANDLOCK_ACCESS_NET_NAMES), + __entry->port) +); + +/** + * landlock_create_domain - New domain created + * + * @domain: Newly created domain (never NULL, immutable after creation). + * @domain->hierarchy->id is its unique ID, shared with the + * landlock_enforce_domain and landlock_free_domain events; + * @domain->hierarchy->details holds the requesting process. + * @ruleset: Source ruleset frozen into the domain (never NULL). The + * ruleset lock is held across the emission, so a BPF program + * reading it via BTF sees the exact merged ruleset; + * @ruleset->id / @ruleset->version identify it. + * + * Emitted by sys_landlock_restrict_self() once, in the requesting + * thread's context, right after the merge and before thread-sync. The + * flags-only path (ruleset_fd == -1) creates no domain and does not + * emit this event. Paired with the per-thread landlock_enforce_domain + * (join on @domain->hierarchy->id) and balanced by a matching + * landlock_free_domain event. + */ +TRACE_EVENT(landlock_create_domain, + + TP_PROTO(const struct landlock_domain *domain, + const struct landlock_ruleset *ruleset), + + TP_ARGS(domain, ruleset), + + TP_STRUCT__entry( + __field( u64, domain_id ) + __field( u64, parent_id ) + __field( u64, ruleset_id ) + __field( u64, ruleset_version ) + ), + + TP_fast_assign( + lockdep_assert_held(&ruleset->lock); + __entry->domain_id = domain->hierarchy->id; + __entry->parent_id = domain->hierarchy->parent ? + domain->hierarchy->parent->id : 0; + __entry->ruleset_id = ruleset->id; + __entry->ruleset_version = ruleset->version; + ), + + TP_printk("domain=%llx parent=%llx ruleset=%llx.%llu", + __entry->domain_id, __entry->parent_id, + __entry->ruleset_id, __entry->ruleset_version) +); + +/** + * landlock_enforce_domain - Domain enforced on a thread + * + * @domain: Domain now enforced on the current thread (never NULL, + * immutable; read locklessly). Correlate to + * landlock_create_domain via @domain->hierarchy->id for the + * source ruleset and requesting thread, or read + * @domain->hierarchy->details for the requesting process. + * @complete: Set on the single event that concludes the operation, after + * all its other enforcements; filter on it for one event per + * operation. + * @process_wide: The enforcement covers every eligible (non-exiting) + * thread of the process: set when the caller used + * %LANDLOCK_RESTRICT_SELF_TSYNC or the process is + * single-threaded. A lone thread whose group still + * holds a zombie leader is not counted single-threaded, + * so process_wide == 0 never proves the opposite. + * @no_new_privs: The enforcing thread's no_new_privs state at + * enforcement time: 1 if set (by a prior + * :manpage:`prctl(2)` %PR_SET_NO_NEW_PRIVS or by + * %LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS), 0 if the domain + * was enforced with %CAP_SYS_ADMIN instead. + * + * Emitted for each thread sys_landlock_restrict_self() enforces the + * domain on, in that thread's own context, right after its + * commit_creds(), so it fires only once the thread is irreversibly + * enforcing the domain (aborted operations emit none). Not + * balanced; every enforcement falls between the domain's + * landlock_create_domain and landlock_free_domain events. + * + * @complete == 1 && @process_wide == 1 means the whole process is + * sandboxed by @domain, durably (Landlock domains are monotonic and + * inherited on :manpage:`clone(2)`). + */ +TRACE_EVENT(landlock_enforce_domain, + + TP_PROTO(const struct landlock_domain *domain, bool complete, + bool process_wide, bool no_new_privs), + + TP_ARGS(domain, complete, process_wide, no_new_privs), + + TP_STRUCT__entry( + __field( u64, domain_id ) + __field( bool, complete ) + __field( bool, process_wide ) + __field( bool, no_new_privs ) + ), + + TP_fast_assign( + __entry->domain_id = domain->hierarchy->id; + __entry->complete = complete; + __entry->process_wide = process_wide; + __entry->no_new_privs = no_new_privs; + ), + + TP_printk("domain=%llx complete=%d process_wide=%d no_new_privs=%d", + __entry->domain_id, __entry->complete, __entry->process_wide, + __entry->no_new_privs) +); + +/** + * landlock_free_domain - Domain freed + * + * @hierarchy: Hierarchy node being freed (never NULL). + * + * Emitted when the hierarchy node's last reference is dropped: its + * refcount reaches zero after all child domains have released their + * parent reference. A committed domain is + * freed from a kworker via landlock_put_domain_deferred() (the credential + * free path runs in RCU context, where sleeping is forbidden), so the + * current task is not the sandboxed task that triggered the free. Balanced + * by a matching landlock_create_domain event. + */ +TRACE_EVENT(landlock_free_domain, + + TP_PROTO(const struct landlock_hierarchy *hierarchy), + + TP_ARGS(hierarchy), + + TP_STRUCT__entry( + __field( u64, domain_id ) + __field( u64, denials ) + ), + + TP_fast_assign( + __entry->domain_id = hierarchy->id; + __entry->denials = atomic64_read(&hierarchy->num_denials); + ), + + TP_printk("domain=%llx denials=%llu", + __entry->domain_id, __entry->denials) +); + +/** + * landlock_check_rule_inode - Inode rule evaluated during access check + * + * @domain: Enforcing domain (never NULL). + * @rule: Matching rule with per-layer access masks (never NULL). + * @access_request: Access mask evaluated against the rule (the domain's + * handled mask during rename/link double-checks). + * @dentry: Filesystem dentry being checked (never NULL). + * + * Emitted for each rule that matches during a filesystem access check. + * The grants array shows the requested rights the rule grants at each + * domain layer. See Documentation/trace/events-landlock.rst for how to + * interpret it. + */ +TRACE_EVENT(landlock_check_rule_inode, + + TP_PROTO(const struct landlock_domain *domain, + const struct landlock_rule *rule, + u64 access_request, const struct dentry *dentry), + + TP_ARGS(domain, rule, access_request, dentry), + + TP_STRUCT__entry( + __field( u64, domain_id ) + __field( access_mask_t, access_request ) + __field( dev_t, dev ) + __field( ino_t, ino ) + __dynamic_array(access_mask_t, grants, + domain->num_layers) + ), + + TP_fast_assign( + __entry->domain_id = domain->hierarchy->id; + __entry->access_request = access_request; + __entry->dev = dentry->d_sb->s_dev; + __entry->ino = d_backing_inode(dentry)->i_ino; + + __trace_landlock_fill_layers(__get_dynamic_array(grants), + __get_dynamic_array_len(grants) / + sizeof(access_mask_t), + rule, + (access_mask_t)access_request); + ), + + TP_printk("domain=%llx access_request=%s dev=%u:%u ino=%lu grants=%s", + __entry->domain_id, + __print_flags(__entry->access_request, "|", _LANDLOCK_ACCESS_FS_NAMES), + MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, + __print_landlock_layers(grants, _LANDLOCK_ACCESS_FS_NAMES)) +); + +/** + * landlock_check_rule_net_port - Network port rule evaluated + * + * @domain: Enforcing domain (never NULL). + * @rule: Matching rule with per-layer access masks (never NULL). + * @access_request: Access mask being requested. + * @port: Network port being checked (host endianness). + * + * Emitted for each rule that matches during a network access check. The + * grants array shows the requested rights the rule grants at each domain + * layer. See Documentation/trace/events-landlock.rst for how to + * interpret it. + */ +TRACE_EVENT(landlock_check_rule_net_port, + + TP_PROTO(const struct landlock_domain *domain, + const struct landlock_rule *rule, + u64 access_request, u64 port), + + TP_ARGS(domain, rule, access_request, port), + + TP_STRUCT__entry( + __field( u64, domain_id ) + __field( access_mask_t, access_request ) + __field( u64, port ) + __dynamic_array(access_mask_t, grants, + domain->num_layers) + ), + + TP_fast_assign( + __entry->domain_id = domain->hierarchy->id; + __entry->access_request = access_request; + __entry->port = port; + + __trace_landlock_fill_layers(__get_dynamic_array(grants), + __get_dynamic_array_len(grants) / + sizeof(access_mask_t), + rule, + (access_mask_t)access_request); + ), + + TP_printk("domain=%llx access_request=%s port=%llu grants=%s", + __entry->domain_id, + __print_flags(__entry->access_request, "|", _LANDLOCK_ACCESS_NET_NAMES), + __entry->port, + __print_landlock_layers(grants, _LANDLOCK_ACCESS_NET_NAMES)) +); + +/** + * landlock_deny_access_fs - Filesystem access denied + * + * @hierarchy: Denying domain's hierarchy node (never NULL); its id is the + * domain field. + * @same_exec: Whether the current task entered the denying domain itself. + * @logged: The domain's audit-logging decision for this denial. + * @blockers: Request type and final missing access subset (never NULL). + * @path: Filesystem path that was denied (never NULL). + * @pathname: Resolved path string (never NULL; an error placeholder on + * resolution failure). + * + * Emitted when a Landlock domain denies a filesystem access. + */ +TRACE_EVENT(landlock_deny_access_fs, + + TP_PROTO(const struct landlock_hierarchy *hierarchy, bool same_exec, + bool logged, const struct landlock_blockers *blockers, + const struct path *path, const char *pathname), + + TP_ARGS(hierarchy, same_exec, logged, blockers, path, pathname), + + TP_STRUCT__entry( + __field( u64, domain_id ) + __field( bool, same_exec ) + __field( bool, logged ) + __field( enum landlock_request_type, blockers_type ) + __field( access_mask_t, blockers_access ) + __field( dev_t, dev ) + __field( ino_t, ino ) + __string( pathname, pathname ) + ), + + TP_fast_assign( + const struct inode *inode = d_backing_inode(path->dentry); + + __entry->domain_id = hierarchy->id; + __entry->same_exec = same_exec; + __entry->logged = logged; + __entry->blockers_type = blockers->type; + __entry->blockers_access = blockers->access; + __entry->dev = path->dentry->d_sb->s_dev; + /* + * A negative dentry has no backing inode, so mirror the + * guard in dump_common_audit_data() and report inode 0. + */ + __entry->ino = inode ? inode->i_ino : 0; + __assign_str(pathname); + ), + + TP_printk("domain=%llx same_exec=%d logged=%d blockers=%s dev=%u:%u ino=%lu path=%s", + __entry->domain_id, __entry->same_exec, __entry->logged, + __entry->blockers_type == LANDLOCK_REQUEST_FS_ACCESS ? + __print_flags(__entry->blockers_access, "|", _LANDLOCK_ACCESS_FS_NAMES) : + __print_symbolic(__entry->blockers_type, + _LANDLOCK_FS_BLOCKER_TYPE_NAMES), + MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, + __trace_print_untrusted_str(p, __get_str(pathname), + __get_dynamic_array_len(pathname) - 1)) +); + +static_assert(offsetof(struct sockaddr_in, sin_port) == + offsetof(struct sockaddr_in6, sin6_port)); +static_assert(sizeof_field(struct sockaddr_in, sin_port) == + sizeof_field(struct sockaddr_in6, sin6_port)); + +/** + * landlock_deny_access_net - Network access denied + * + * @hierarchy: Denying domain's hierarchy node (never NULL); its id is the + * domain field. + * @same_exec: Whether the current task entered the denying domain itself. + * @logged: The domain's audit-logging decision for this denial. + * @blockers: Request type and final missing access subset (never NULL). + * @sk: Socket object (never NULL), read without a socket lock, so its fields + * are a best-effort snapshot. + * @socket_family: Socket-family snapshot used by the verdict. + * @address: Authoritative address checked by the verdict (never NULL). + * The producer copies @addrlen bytes from the checked address and + * zeroes the remaining storage before emission. The + * &sockaddr_in.sin_port or &sockaddr_in6.sin6_port member, when + * present, remains in network endianness. + * @addrlen: Validated signed length of @address. + * + * Emitted when a Landlock domain denies a network operation. The blocker + * identifies whether the address is a bind or connect/send policy object. + * The flattened port field is converted from the checked address to host + * endianness, or is -1 when no port was checked. Zero is a valid checked + * port. + */ +TRACE_EVENT(landlock_deny_access_net, + + TP_PROTO(const struct landlock_hierarchy *hierarchy, bool same_exec, + bool logged, const struct landlock_blockers *blockers, + const struct sock *sk, u16 socket_family, + const struct sockaddr_storage *address, int addrlen), + + TP_ARGS(hierarchy, same_exec, logged, blockers, sk, socket_family, + address, addrlen), + + TP_STRUCT__entry( + __field( u64, domain_id ) + __field( bool, same_exec ) + __field( bool, logged ) + __field( enum landlock_request_type, blockers_type ) + __field( access_mask_t, blockers_access ) + __field( s64, port ) + ), + + TP_fast_assign( + const struct sockaddr *const addr = + (const struct sockaddr *)address; + const bool has_port = + addrlen >= (int)offsetofend(struct sockaddr_in, sin_port) && + (addr->sa_family == AF_INET || + addr->sa_family == AF_INET6 || + (addr->sa_family == AF_UNSPEC && + socket_family == AF_INET)); + + __entry->domain_id = hierarchy->id; + __entry->same_exec = same_exec; + __entry->logged = logged; + __entry->blockers_type = blockers->type; + __entry->blockers_access = blockers->access; + __entry->port = + has_port ? + ntohs(((const struct sockaddr_in *)addr)->sin_port) : + -1; + ), + + TP_printk("domain=%llx same_exec=%d logged=%d blockers=%s port=%lld", + __entry->domain_id, __entry->same_exec, __entry->logged, + __entry->blockers_type == LANDLOCK_REQUEST_NET_ACCESS ? + __print_flags(__entry->blockers_access, "|", _LANDLOCK_ACCESS_NET_NAMES) : + "unknown", + __entry->port) +); + +/** + * landlock_deny_ptrace - Ptrace access denied by a Landlock domain + * + * @hierarchy: Denying domain's hierarchy node (never NULL); its id is the + * domain field. + * @same_exec: Whether the tracer entered the denying domain itself. + * @logged: The domain's audit-logging decision for this denial. + * @tracee_domain_id: The tracee's Landlock domain ID, or 0 if the tracee + * is unsandboxed. + * @tracee: The target task ptrace acted on (never NULL). tracee_pid is + * the init-namespace TGID (like audit's opid). + * @tracer: The tracer or proposed tracer (never NULL); for PTRACE_TRACEME + * this is the parent, not the syscall caller. + * + * Emitted when a Landlock domain denies a ptrace operation. + */ +TRACE_EVENT(landlock_deny_ptrace, + + TP_PROTO(const struct landlock_hierarchy *hierarchy, bool same_exec, + bool logged, u64 tracee_domain_id, + const struct task_struct *tracee, + const struct task_struct *tracer), + + TP_ARGS(hierarchy, same_exec, logged, tracee_domain_id, tracee, tracer), + + TP_STRUCT__entry( + __field( u64, domain_id ) + __field( bool, same_exec ) + __field( bool, logged ) + __field( u64, tracee_domain_id) + __field( pid_t, tracee_pid ) + __string( tracee_comm, tracee->comm ) + ), + + TP_fast_assign( + __entry->domain_id = hierarchy->id; + __entry->same_exec = same_exec; + __entry->logged = logged; + __entry->tracee_domain_id = tracee_domain_id; + __entry->tracee_pid = task_tgid_nr((struct task_struct *)tracee); + __assign_str(tracee_comm); + ), + + TP_printk("domain=%llx same_exec=%d logged=%d tracee_domain=%llx tracee_pid=%d tracee_comm=%s", + __entry->domain_id, __entry->same_exec, __entry->logged, + __entry->tracee_domain_id, __entry->tracee_pid, + __trace_print_untrusted_str(p, __get_str(tracee_comm), + __get_dynamic_array_len(tracee_comm) - 1)) +); + +/** + * landlock_deny_scope_signal - Signal delivery denied by + * LANDLOCK_SCOPE_SIGNAL + * + * @hierarchy: Denying domain's hierarchy node (never NULL); its id is the + * domain field. + * @same_exec: Whether the policy subject entered the denying domain itself. + * @logged: The domain's audit-logging decision for this denial. + * @target_domain_id: The target's Landlock domain ID, or 0 if the target + * is unsandboxed. + * @target: The task the signal was aimed at (never NULL). target_pid is + * the init-namespace TGID (like audit's opid). + * @signal: The signal selected by the denied check. Zero is a permission + * probe, not an absent value. + * + * Emitted when a Landlock domain denies signal delivery to a scoped-out + * target. + */ +TRACE_EVENT(landlock_deny_scope_signal, + + TP_PROTO(const struct landlock_hierarchy *hierarchy, bool same_exec, + bool logged, u64 target_domain_id, + const struct task_struct *target, int signal), + + TP_ARGS(hierarchy, same_exec, logged, target_domain_id, target, signal), + + TP_STRUCT__entry( + __field( u64, domain_id ) + __field( bool, same_exec ) + __field( bool, logged ) + __field( u64, target_domain_id) + __field( pid_t, target_pid ) + __string( target_comm, target->comm ) + ), + + TP_fast_assign( + __entry->domain_id = hierarchy->id; + __entry->same_exec = same_exec; + __entry->logged = logged; + __entry->target_domain_id = target_domain_id; + __entry->target_pid = task_tgid_nr((struct task_struct *)target); + __assign_str(target_comm); + ), + + TP_printk("domain=%llx same_exec=%d logged=%d target_domain=%llx target_pid=%d target_comm=%s", + __entry->domain_id, __entry->same_exec, __entry->logged, + __entry->target_domain_id, __entry->target_pid, + __trace_print_untrusted_str(p, __get_str(target_comm), + __get_dynamic_array_len(target_comm) - 1)) +); + +/** + * landlock_deny_scope_abstract_unix_socket - Abstract unix socket access + * denied by LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET + * + * @hierarchy: Denying domain's hierarchy node (never NULL); its id is the + * domain field. + * @same_exec: Whether the current task entered the denying domain itself. + * @logged: The domain's audit-logging decision for this denial. + * @peer_domain_id: The peer's Landlock domain ID, or 0 if the peer is + * unsandboxed. + * @peer: Peer socket (never NULL). peer_pid is best-effort: it is 0 for + * a datagram peer (no SO_PEERCRED), so sun_path is the reliable + * peer identifier. + * + * Emitted when a Landlock domain denies access to a scoped-out abstract + * unix socket. + */ +TRACE_EVENT(landlock_deny_scope_abstract_unix_socket, + + TP_PROTO(const struct landlock_hierarchy *hierarchy, bool same_exec, + bool logged, u64 peer_domain_id, const struct sock *peer), + + TP_ARGS(hierarchy, same_exec, logged, peer_domain_id, peer), + + TP_STRUCT__entry( + __field( u64, domain_id ) + __field( bool, same_exec ) + __field( bool, logged ) + __field( u64, peer_domain_id ) + __field( pid_t, peer_pid ) + /* + * Abstract socket names are untrusted binary data from + * user space. Use __string_len because abstract names + * are not NUL-terminated; their length is determined by + * addr->len. unix_sk(peer)->addr is stable here because + * the caller (hook_unix_stream_connect or + * hook_unix_may_send) holds unix_state_lock(peer). + */ + __string_len( sun_path, + unix_sk(peer)->addr ? + unix_sk(peer)->addr->name->sun_path + 1 : + "", + unix_sk(peer)->addr ? + unix_sk(peer)->addr->len - + offsetof(struct sockaddr_un, + sun_path) - 1 : + 0) + ), + + TP_fast_assign( + struct pid *peer_pid; + + lockdep_assert_held(&unix_sk(peer)->lock); + __entry->domain_id = hierarchy->id; + __entry->same_exec = same_exec; + __entry->logged = logged; + __entry->peer_domain_id = peer_domain_id; + /* + * Best-effort (0 for a datagram peer). The caller holds the + * peer's AF_UNIX state lock, serializing published peercred + * updates. The peer socket keeps a reference to sk_peer_pid + * through pid_nr(); sun_path is the reliable identifier. + */ + peer_pid = READ_ONCE(peer->sk_peer_pid); + __entry->peer_pid = peer_pid ? pid_nr(peer_pid) : 0; + __assign_str(sun_path); + ), + + TP_printk("domain=%llx same_exec=%d logged=%d peer_domain=%llx peer_pid=%d sun_path=%s", + __entry->domain_id, __entry->same_exec, __entry->logged, + __entry->peer_domain_id, __entry->peer_pid, + __trace_print_untrusted_str(p, __get_str(sun_path), + __get_dynamic_array_len(sun_path) - 1)) +); + +#undef _LANDLOCK_FS_BLOCKER_TYPE_NAMES +#undef _LANDLOCK_NAME_ENTRY + +#endif /* _TRACE_LANDLOCK_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h> + +/* clang-format on */ diff --git a/include/trace/events/lock.h b/include/trace/events/lock.h index 1ded869cd619..b1d5b18c4514 100644 --- a/include/trace/events/lock.h +++ b/include/trace/events/lock.h @@ -137,7 +137,11 @@ TRACE_EVENT(contention_end, TP_printk("%p (ret=%d)", __entry->lock_addr, __entry->ret) ); -TRACE_EVENT(contended_release, +/* kernel/locking/mutex.c */ +int arch_contended_release_trace_reg(void); +void arch_contended_release_trace_unreg(void); + +TRACE_EVENT_FN(contended_release, TP_PROTO(void *lock), @@ -151,7 +155,9 @@ TRACE_EVENT(contended_release, __entry->lock_addr = lock; ), - TP_printk("%p", __entry->lock_addr) + TP_printk("%p", __entry->lock_addr), + + arch_contended_release_trace_reg, arch_contended_release_trace_unreg ); #endif /* _TRACE_LOCK_H */ diff --git a/include/trace/events/memory-failure.h b/include/trace/events/memory-failure.h index aa57cc8f896b..7a8ee5d1a44e 100644 --- a/include/trace/events/memory-failure.h +++ b/include/trace/events/memory-failure.h @@ -1,6 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0 */ #undef TRACE_SYSTEM -#define TRACE_SYSTEM memory_failure +/* + * For historical versions, memory_failure_event is in ras subsystem, + * some user programs depend on it. + */ +#define TRACE_SYSTEM ras #define TRACE_INCLUDE_FILE memory-failure #if !defined(_TRACE_MEMORY_FAILURE_H) || defined(TRACE_HEADER_MULTI_READ) diff --git a/include/trace/events/migrate.h b/include/trace/events/migrate.h index cd01dd7b3640..15ee2ef201b5 100644 --- a/include/trace/events/migrate.h +++ b/include/trace/events/migrate.h @@ -23,7 +23,8 @@ EM( MR_CONTIG_RANGE, "contig_range") \ EM( MR_LONGTERM_PIN, "longterm_pin") \ EM( MR_DEMOTION, "demotion") \ - EMe(MR_DAMON, "damon") + EM( MR_DAMON, "damon") \ + EMe(MR_NEVER, "never_migrated") /* * First define the enums in the above macros to be exported to userspace @@ -51,7 +52,7 @@ TRACE_EVENT(mm_migrate_pages, TP_PROTO(unsigned long succeeded, unsigned long failed, unsigned long thp_succeeded, unsigned long thp_failed, unsigned long thp_split, unsigned long large_folio_split, - enum migrate_mode mode, int reason), + enum migrate_mode mode, enum migrate_reason reason), TP_ARGS(succeeded, failed, thp_succeeded, thp_failed, thp_split, large_folio_split, mode, reason), @@ -64,7 +65,7 @@ TRACE_EVENT(mm_migrate_pages, __field( unsigned long, thp_split) __field( unsigned long, large_folio_split) __field( enum migrate_mode, mode) - __field( int, reason) + __field( enum migrate_reason, reason) ), TP_fast_assign( @@ -91,13 +92,13 @@ TRACE_EVENT(mm_migrate_pages, TRACE_EVENT(mm_migrate_pages_start, - TP_PROTO(enum migrate_mode mode, int reason), + TP_PROTO(enum migrate_mode mode, enum migrate_reason reason), TP_ARGS(mode, reason), TP_STRUCT__entry( __field(enum migrate_mode, mode) - __field(int, reason) + __field(enum migrate_reason, reason) ), TP_fast_assign( diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h index a6e5a44c9b42..935893e5ea53 100644 --- a/include/trace/events/mmflags.h +++ b/include/trace/events/mmflags.h @@ -54,18 +54,10 @@ # define TRACE_GFP_FLAGS_LOCKDEP #endif -#ifdef CONFIG_SLAB_OBJ_EXT -# define TRACE_GFP_FLAGS_SLAB \ - TRACE_GFP_EM(NO_OBJ_EXT) -#else -# define TRACE_GFP_FLAGS_SLAB -#endif - #define TRACE_GFP_FLAGS \ TRACE_GFP_FLAGS_GENERAL \ TRACE_GFP_FLAGS_KASAN \ - TRACE_GFP_FLAGS_LOCKDEP \ - TRACE_GFP_FLAGS_SLAB + TRACE_GFP_FLAGS_LOCKDEP #undef TRACE_GFP_EM #define TRACE_GFP_EM(a) TRACE_DEFINE_ENUM(___GFP_##a##_BIT); @@ -194,6 +186,12 @@ IF_HAVE_PG_ARCH_3(arch_3) # define IF_HAVE_UFFD_MINOR(flag, name) #endif +#ifdef CONFIG_USERFAULTFD_RWP +# define IF_HAVE_UFFD_RWP(flag, name) {flag, name}, +#else +# define IF_HAVE_UFFD_RWP(flag, name) +#endif + #if defined(CONFIG_64BIT) || defined(CONFIG_PPC32) # define IF_HAVE_VM_DROPPABLE(flag, name) {flag, name}, #else @@ -215,6 +213,7 @@ IF_HAVE_UFFD_MINOR(VM_UFFD_MINOR, "uffd_minor" ) \ {VM_PFNMAP, "pfnmap" }, \ {VM_MAYBE_GUARD, "maybe_guard" }, \ {VM_UFFD_WP, "uffd_wp" }, \ +IF_HAVE_UFFD_RWP(VM_UFFD_RWP, "uffd_rwp" ) \ {VM_LOCKED, "locked" }, \ {VM_IO, "io" }, \ {VM_SEQ_READ, "seqread" }, \ diff --git a/include/trace/events/mptcp.h b/include/trace/events/mptcp.h index 04521acba483..22882bd03459 100644 --- a/include/trace/events/mptcp.h +++ b/include/trace/events/mptcp.h @@ -75,7 +75,6 @@ DECLARE_EVENT_CLASS(mptcp_dump_mpext, TP_ARGS(mpext), TP_STRUCT__entry( - __field(u64, data_ack) __field(u64, data_seq) __field(u32, subflow_seq) __field(u16, data_len) @@ -94,7 +93,6 @@ DECLARE_EVENT_CLASS(mptcp_dump_mpext, ), TP_fast_assign( - __entry->data_ack = mpext->ack64 ? mpext->data_ack : mpext->data_ack32; __entry->data_seq = mpext->data_seq; __entry->subflow_seq = mpext->subflow_seq; __entry->data_len = mpext->data_len; @@ -112,8 +110,8 @@ DECLARE_EVENT_CLASS(mptcp_dump_mpext, __entry->infinite_map = mpext->infinite_map; ), - TP_printk("data_ack=%llu data_seq=%llu subflow_seq=%u data_len=%u csum=%x use_map=%u dsn64=%u data_fin=%u use_ack=%u ack64=%u mpc_map=%u frozen=%u reset_transient=%u reset_reason=%u csum_reqd=%u infinite_map=%u", - __entry->data_ack, __entry->data_seq, + TP_printk("data_seq=%llu subflow_seq=%u data_len=%u csum=%x use_map=%u dsn64=%u data_fin=%u use_ack=%u ack64=%u mpc_map=%u frozen=%u reset_transient=%u reset_reason=%u csum_reqd=%u infinite_map=%u", + __entry->data_seq, __entry->subflow_seq, __entry->data_len, __entry->csum, __entry->use_map, __entry->dsn64, __entry->data_fin, diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h index 082cb03c6131..3fec3e8f91c8 100644 --- a/include/trace/events/netfs.h +++ b/include/trace/events/netfs.h @@ -59,6 +59,7 @@ EM(netfs_rreq_trace_free, "FREE ") \ EM(netfs_rreq_trace_intr, "INTR ") \ EM(netfs_rreq_trace_ki_complete, "KI-CMPL") \ + EM(netfs_rreq_trace_ra_put_ref, "RA-PUT ") \ EM(netfs_rreq_trace_recollect, "RECLLCT") \ EM(netfs_rreq_trace_redirty, "REDIRTY") \ EM(netfs_rreq_trace_resubmit, "RESUBMT") \ @@ -70,9 +71,11 @@ EM(netfs_rreq_trace_unpause, "UNPAUSE") \ EM(netfs_rreq_trace_wait_ip, "WAIT-IP") \ EM(netfs_rreq_trace_wait_pause, "--PAUSED--") \ + EM(netfs_rreq_trace_wait_put_ra_refs, "WAIT-P-RA") \ EM(netfs_rreq_trace_wait_quiesce, "WAIT-QUIESCE") \ EM(netfs_rreq_trace_waited_ip, "DONE-IP") \ EM(netfs_rreq_trace_waited_pause, "--UNPAUSED--") \ + EM(netfs_rreq_trace_waited_put_ra_refs, "DONE-P-RA") \ EM(netfs_rreq_trace_waited_quiesce, "DONE-QUIESCE") \ EM(netfs_rreq_trace_wake_ip, "WAKE-IP") \ EM(netfs_rreq_trace_wake_queue, "WAKE-Q ") \ @@ -195,7 +198,6 @@ EM(netfs_folio_trace_clear_cc, "clear-cc") \ EM(netfs_folio_trace_clear_g, "clear-g") \ EM(netfs_folio_trace_clear_s, "clear-s") \ - EM(netfs_folio_trace_copy_to_cache, "mark-copy") \ EM(netfs_folio_trace_end_copy, "end-copy") \ EM(netfs_folio_trace_filled_gaps, "filled-gaps") \ EM(netfs_folio_trace_invalidate_all, "inval-all") \ @@ -206,16 +208,19 @@ EM(netfs_folio_trace_kill_cc, "kill-cc") \ EM(netfs_folio_trace_kill_g, "kill-g") \ EM(netfs_folio_trace_kill_s, "kill-s") \ + EM(netfs_folio_trace_mark_copy, "mark-copy") \ EM(netfs_folio_trace_mkwrite, "mkwrite") \ EM(netfs_folio_trace_mkwrite_plus, "mkwrite+") \ - EM(netfs_folio_trace_not_under_wback, "!wback") \ EM(netfs_folio_trace_not_locked, "!locked") \ + EM(netfs_folio_trace_not_under_wback, "!wback") \ + EM(netfs_folio_trace_pgpriv2_copy, "pgpriv2-copy") \ EM(netfs_folio_trace_put, "put") \ EM(netfs_folio_trace_read, "read") \ EM(netfs_folio_trace_read_done, "read-done") \ EM(netfs_folio_trace_read_gaps, "read-gaps") \ EM(netfs_folio_trace_read_unlock, "read-unlock") \ EM(netfs_folio_trace_redirtied, "redirtied") \ + EM(netfs_folio_trace_sched_copy, "sched-copy") \ EM(netfs_folio_trace_store, "store") \ EM(netfs_folio_trace_store_copy, "store-copy") \ EM(netfs_folio_trace_store_plus, "store+") \ @@ -786,6 +791,27 @@ TRACE_EVENT(netfs_folioq, __print_symbolic(__entry->trace, netfs_folioq_traces)) ); +TRACE_EVENT(netfs_read_progress_at, + TP_PROTO(const struct netfs_io_request *rreq), + + TP_ARGS(rreq), + + TP_STRUCT__entry( + __field(unsigned int, rreq) + __field(size_t, progress_at) + __field(size_t, cleaned_to) + ), + + TP_fast_assign( + __entry->rreq = rreq->debug_id; + __entry->cleaned_to = rreq->cleaned_to - rreq->start; + __entry->progress_at = rreq->progress_at; + ), + + TP_printk("R=%08x cln=%zx prg=%zx", + __entry->rreq, __entry->cleaned_to, __entry->progress_at) + ); + #undef EM #undef E_ #endif /* _TRACE_NETFS_H */ diff --git a/include/trace/events/pagemap.h b/include/trace/events/pagemap.h index 171524d3526d..36c3a90f0acc 100644 --- a/include/trace/events/pagemap.h +++ b/include/trace/events/pagemap.h @@ -77,6 +77,14 @@ TRACE_EVENT(mm_lru_activate, TP_printk("folio=%p pfn=0x%lx", __entry->folio, __entry->pfn) ); +DECLARE_TRACE(mm_lru_add_drain, + TP_PROTO(int cpu, unsigned int nr_folios), + TP_ARGS(cpu, nr_folios)); + +DECLARE_TRACE(mm_lru_add_drain_all, + TP_PROTO(bool force_all_cpus), + TP_ARGS(force_all_cpus)); + #endif /* _TRACE_PAGEMAP_H */ /* This part must be outside protection */ diff --git a/include/trace/events/qcom_geni_i2c.h b/include/trace/events/qcom_geni_i2c.h new file mode 100644 index 000000000000..c7e7984f3620 --- /dev/null +++ b/include/trace/events/qcom_geni_i2c.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM qcom_geni_i2c + +#if !defined(_TRACE_QCOM_GENI_I2C_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_QCOM_GENI_I2C_H + +#include <linux/tracepoint.h> + +TRACE_EVENT(geni_i2c_bus_setup, + TP_PROTO(struct device *dev, u32 clk_freq, u8 clk_div, + u8 t_high_cnt, u8 t_low_cnt, u8 t_cycle_cnt), + TP_ARGS(dev, clk_freq, clk_div, t_high_cnt, t_low_cnt, t_cycle_cnt), + + TP_STRUCT__entry(__string(name, dev_name(dev)) + __field(u32, clk_freq) + __field(u8, clk_div) + __field(u8, t_high_cnt) + __field(u8, t_low_cnt) + __field(u8, t_cycle_cnt) + ), + + TP_fast_assign(__assign_str(name); + __entry->clk_freq = clk_freq; + __entry->clk_div = clk_div; + __entry->t_high_cnt = t_high_cnt; + __entry->t_low_cnt = t_low_cnt; + __entry->t_cycle_cnt = t_cycle_cnt; + ), + + TP_printk("%s: clk_freq=%u clk_div=%u t_high=%u t_low=%u t_cycle=%u", + __get_str(name), __entry->clk_freq, __entry->clk_div, + __entry->t_high_cnt, __entry->t_low_cnt, + __entry->t_cycle_cnt) +); + +TRACE_EVENT(geni_i2c_irq, + TP_PROTO(struct device *dev, u32 m_stat, u32 rx_st, + u32 dm_tx_st, u32 dm_rx_st), + TP_ARGS(dev, m_stat, rx_st, dm_tx_st, dm_rx_st), + + TP_STRUCT__entry(__string(name, dev_name(dev)) + __field(u32, m_stat) + __field(u32, rx_st) + __field(u32, dm_tx_st) + __field(u32, dm_rx_st) + ), + + TP_fast_assign(__assign_str(name); + __entry->m_stat = m_stat; + __entry->rx_st = rx_st; + __entry->dm_tx_st = dm_tx_st; + __entry->dm_rx_st = dm_rx_st; + ), + + TP_printk("%s: m_stat=0x%08x rx_st=0x%08x dm_tx=0x%08x dm_rx=0x%08x", + __get_str(name), __entry->m_stat, __entry->rx_st, + __entry->dm_tx_st, __entry->dm_rx_st) +); + +TRACE_EVENT(geni_i2c_err, + TP_PROTO(struct device *dev, int err, const char *msg), + TP_ARGS(dev, err, msg), + + TP_STRUCT__entry(__string(name, dev_name(dev)) + __field(int, err) + __string(msg, msg) + ), + + TP_fast_assign(__assign_str(name); + __entry->err = err; + __assign_str(msg); + ), + + TP_printk("%s: err=%d msg=%s", + __get_str(name), __entry->err, __get_str(msg)) +); + +#endif /* _TRACE_QCOM_GENI_I2C_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h> diff --git a/include/trace/events/qcom_geni_se.h b/include/trace/events/qcom_geni_se.h new file mode 100644 index 000000000000..4a6e1ba2d147 --- /dev/null +++ b/include/trace/events/qcom_geni_se.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM qcom_geni_se + +#if !defined(_TRACE_QCOM_GENI_SE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_QCOM_GENI_SE_H + +#include <linux/io.h> +#include <linux/tracepoint.h> +#include <linux/soc/qcom/geni-se.h> + +TRACE_EVENT(geni_se_regs, + TP_PROTO(struct geni_se *se), + + TP_ARGS(se), + + TP_STRUCT__entry(__string(geni_se_name, dev_name(se->dev)) + __field(u32, geni_se_m_cmd0) + __field(u32, geni_se_m_irq_status) + __field(u32, geni_se_s_cmd0) + __field(u32, geni_se_s_irq_status) + __field(u32, geni_se_status) + __field(u32, geni_se_ios) + __field(u32, geni_se_m_cmd_ctrl) + __field(u32, geni_se_m_cmd_err) + __field(u32, geni_se_m_fw_err) + __field(u32, geni_se_tx_fifo_status) + __field(u32, geni_se_rx_fifo_status) + __field(u32, geni_se_tx_watermark) + __field(u32, geni_se_rx_watermark) + __field(u32, geni_se_rx_watermark_rfr) + __field(u32, geni_se_m_gp_length) + __field(u32, geni_se_s_gp_length) + __field(u32, geni_se_dma_tx_irq) + __field(u32, geni_se_dma_rx_irq) + __field(u32, geni_se_dma_tx_irq_en) + __field(u32, geni_se_dma_rx_irq_en) + __field(u32, geni_se_dma_rx_len) + __field(u32, geni_se_dma_rx_len_in) + __field(u32, geni_se_dma_tx_len) + __field(u32, geni_se_dma_tx_len_in) + __field(u32, geni_se_dma_tx_ptr_l) + __field(u32, geni_se_dma_tx_ptr_h) + __field(u32, geni_se_dma_rx_ptr_l) + __field(u32, geni_se_dma_rx_ptr_h) + __field(u32, geni_se_dma_tx_attr) + __field(u32, geni_se_dma_tx_max_burst) + __field(u32, geni_se_dma_rx_attr) + __field(u32, geni_se_dma_rx_max_burst) + __field(u32, geni_se_dma_if_en) + __field(u32, geni_se_dma_if_en_ro) + __field(u32, geni_se_dma_general_cfg) + __field(u32, geni_se_dma_qsb_trans_cfg) + __field(u32, geni_se_dma_dbg) + __field(u32, geni_se_m_irq_en) + __field(u32, geni_se_s_irq_en) + __field(u32, geni_se_gsi_event_en) + __field(u32, geni_se_irq_en) + __field(u32, geni_se_ser_m_clk_cfg) + __field(u32, geni_se_ser_s_clk_cfg) + __field(u32, geni_se_general_cfg) + __field(u32, geni_se_output_ctrl) + __field(u32, geni_se_clk_ctrl_ro) + __field(u32, geni_se_fifo_if_disable) + __field(u32, geni_se_fw_multilock_msa) + __field(u32, geni_se_clk_sel) + ), + + TP_fast_assign(__assign_str(geni_se_name); + __entry->geni_se_m_cmd0 = readl(se->base + SE_GENI_M_CMD0); + __entry->geni_se_m_irq_status = readl(se->base + SE_GENI_M_IRQ_STATUS); + __entry->geni_se_s_cmd0 = readl(se->base + SE_GENI_S_CMD0); + __entry->geni_se_s_irq_status = readl(se->base + SE_GENI_S_IRQ_STATUS); + __entry->geni_se_status = readl(se->base + SE_GENI_STATUS); + __entry->geni_se_ios = readl(se->base + SE_GENI_IOS); + __entry->geni_se_m_cmd_ctrl = readl(se->base + SE_GENI_M_CMD_CTRL_REG); + __entry->geni_se_m_cmd_err = readl(se->base + M_CMD_ERR_STATUS); + __entry->geni_se_m_fw_err = readl(se->base + M_FW_ERR_STATUS); + __entry->geni_se_tx_fifo_status = readl(se->base + SE_GENI_TX_FIFO_STATUS); + __entry->geni_se_rx_fifo_status = readl(se->base + SE_GENI_RX_FIFO_STATUS); + __entry->geni_se_tx_watermark = readl(se->base + SE_GENI_TX_WATERMARK_REG); + __entry->geni_se_rx_watermark = readl(se->base + SE_GENI_RX_WATERMARK_REG); + __entry->geni_se_rx_watermark_rfr = readl(se->base + SE_GENI_RX_RFR_WATERMARK_REG); + __entry->geni_se_m_gp_length = readl(se->base + SE_GENI_M_GP_LENGTH); + __entry->geni_se_s_gp_length = readl(se->base + SE_GENI_S_GP_LENGTH); + __entry->geni_se_dma_tx_irq = readl(se->base + SE_DMA_TX_IRQ_STAT); + __entry->geni_se_dma_rx_irq = readl(se->base + SE_DMA_RX_IRQ_STAT); + __entry->geni_se_dma_tx_irq_en = readl(se->base + SE_DMA_TX_IRQ_EN); + __entry->geni_se_dma_rx_irq_en = readl(se->base + SE_DMA_RX_IRQ_EN); + __entry->geni_se_dma_rx_len = readl(se->base + SE_DMA_RX_LEN); + __entry->geni_se_dma_rx_len_in = readl(se->base + SE_DMA_RX_LEN_IN); + __entry->geni_se_dma_tx_len = readl(se->base + SE_DMA_TX_LEN); + __entry->geni_se_dma_tx_len_in = readl(se->base + SE_DMA_TX_LEN_IN); + __entry->geni_se_dma_tx_ptr_l = readl(se->base + SE_DMA_TX_PTR_L); + __entry->geni_se_dma_tx_ptr_h = readl(se->base + SE_DMA_TX_PTR_H); + __entry->geni_se_dma_rx_ptr_l = readl(se->base + SE_DMA_RX_PTR_L); + __entry->geni_se_dma_rx_ptr_h = readl(se->base + SE_DMA_RX_PTR_H); + __entry->geni_se_dma_tx_attr = readl(se->base + SE_DMA_TX_ATTR); + __entry->geni_se_dma_tx_max_burst = readl(se->base + SE_DMA_TX_MAX_BURST); + __entry->geni_se_dma_rx_attr = readl(se->base + SE_DMA_RX_ATTR); + __entry->geni_se_dma_rx_max_burst = readl(se->base + SE_DMA_RX_MAX_BURST); + __entry->geni_se_dma_if_en = readl(se->base + SE_DMA_IF_EN); + __entry->geni_se_dma_if_en_ro = readl(se->base + DMA_IF_EN_RO); + __entry->geni_se_dma_general_cfg = readl(se->base + DMA_GENERAL_CFG); + __entry->geni_se_dma_qsb_trans_cfg = readl(se->base + SE_DMA_QSB_TRANS_CFG); + __entry->geni_se_dma_dbg = readl(se->base + SE_DMA_DEBUG_REG0); + __entry->geni_se_m_irq_en = readl(se->base + SE_GENI_M_IRQ_EN); + __entry->geni_se_s_irq_en = readl(se->base + SE_GENI_S_IRQ_EN); + __entry->geni_se_gsi_event_en = readl(se->base + SE_GSI_EVENT_EN); + __entry->geni_se_irq_en = readl(se->base + SE_IRQ_EN); + __entry->geni_se_ser_m_clk_cfg = readl(se->base + GENI_SER_M_CLK_CFG); + __entry->geni_se_ser_s_clk_cfg = readl(se->base + GENI_SER_S_CLK_CFG); + __entry->geni_se_general_cfg = readl(se->base + GENI_GENERAL_CFG); + __entry->geni_se_output_ctrl = readl(se->base + GENI_OUTPUT_CTRL); + __entry->geni_se_clk_ctrl_ro = readl(se->base + GENI_CLK_CTRL_RO); + __entry->geni_se_fifo_if_disable = readl(se->base + GENI_IF_DISABLE_RO); + __entry->geni_se_fw_multilock_msa = readl(se->base + GENI_FW_MULTILOCK_MSA_RO); + __entry->geni_se_clk_sel = readl(se->base + SE_GENI_CLK_SEL); + ), + + TP_printk("%s: m_cmd0=0x%08x m_irq_status=0x%08x s_cmd0=0x%08x s_irq_status=0x%08x geni_status=0x%08x geni_ios=0x%08x m_cmd_ctrl=0x%08x m_cmd_err=0x%08x m_fw_err=0x%08x tx_fifo_sts=0x%08x rx_fifo_sts=0x%08x tx_watermark=0x%08x rx_watermark=0x%08x rx_watermark_rfr=0x%08x m_gp_length=0x%08x s_gp_length=0x%08x dma_tx_irq=0x%08x dma_rx_irq=0x%08x dma_tx_irq_en=0x%08x dma_rx_irq_en=0x%08x dma_rx_len=0x%08x dma_rx_len_in=0x%08x dma_tx_len=0x%08x dma_tx_len_in=0x%08x dma_tx_ptr_l=0x%08x dma_tx_ptr_h=0x%08x dma_rx_ptr_l=0x%08x dma_rx_ptr_h=0x%08x dma_tx_attr=0x%08x dma_tx_max_burst=0x%08x dma_rx_attr=0x%08x dma_rx_max_burst=0x%08x dma_if_en=0x%08x dma_if_en_ro=0x%08x dma_general_cfg=0x%08x dma_qsb_trans_cfg=0x%08x dma_dbg=0x%08x m_irq_en=0x%08x s_irq_en=0x%08x gsi_event_en=0x%08x se_irq_en=0x%08x ser_m_clk_cfg=0x%08x ser_s_clk_cfg=0x%08x general_cfg=0x%08x output_ctrl=0x%08x clk_ctrl_ro=0x%08x fifo_if_dis=0x%08x fw_multilock_msa=0x%08x clk_sel=0x%08x", + __get_str(geni_se_name), + __entry->geni_se_m_cmd0, __entry->geni_se_m_irq_status, + __entry->geni_se_s_cmd0, __entry->geni_se_s_irq_status, + __entry->geni_se_status, __entry->geni_se_ios, + __entry->geni_se_m_cmd_ctrl, + __entry->geni_se_m_cmd_err, __entry->geni_se_m_fw_err, + __entry->geni_se_tx_fifo_status, __entry->geni_se_rx_fifo_status, + __entry->geni_se_tx_watermark, __entry->geni_se_rx_watermark, + __entry->geni_se_rx_watermark_rfr, + __entry->geni_se_m_gp_length, __entry->geni_se_s_gp_length, + __entry->geni_se_dma_tx_irq, __entry->geni_se_dma_rx_irq, + __entry->geni_se_dma_tx_irq_en, __entry->geni_se_dma_rx_irq_en, + __entry->geni_se_dma_rx_len, __entry->geni_se_dma_rx_len_in, + __entry->geni_se_dma_tx_len, __entry->geni_se_dma_tx_len_in, + __entry->geni_se_dma_tx_ptr_l, __entry->geni_se_dma_tx_ptr_h, + __entry->geni_se_dma_rx_ptr_l, __entry->geni_se_dma_rx_ptr_h, + __entry->geni_se_dma_tx_attr, __entry->geni_se_dma_tx_max_burst, + __entry->geni_se_dma_rx_attr, __entry->geni_se_dma_rx_max_burst, + __entry->geni_se_dma_if_en, __entry->geni_se_dma_if_en_ro, + __entry->geni_se_dma_general_cfg, __entry->geni_se_dma_qsb_trans_cfg, + __entry->geni_se_dma_dbg, + __entry->geni_se_m_irq_en, __entry->geni_se_s_irq_en, + __entry->geni_se_gsi_event_en, __entry->geni_se_irq_en, + __entry->geni_se_ser_m_clk_cfg, __entry->geni_se_ser_s_clk_cfg, + __entry->geni_se_general_cfg, __entry->geni_se_output_ctrl, + __entry->geni_se_clk_ctrl_ro, __entry->geni_se_fifo_if_disable, + __entry->geni_se_fw_multilock_msa, __entry->geni_se_clk_sel) +); + +#endif /* _TRACE_QCOM_GENI_SE_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h> diff --git a/include/trace/events/qcom_geni_serial.h b/include/trace/events/qcom_geni_serial.h index 417ec01f9fc8..e1aa551d525e 100644 --- a/include/trace/events/qcom_geni_serial.h +++ b/include/trace/events/qcom_geni_serial.h @@ -97,18 +97,17 @@ DECLARE_EVENT_CLASS(geni_serial_data, TP_ARGS(dev, buf, len), TP_STRUCT__entry(__string(name, dev_name(dev)) - __field(unsigned int, len) __dynamic_array(u8, data, len) ), TP_fast_assign(__assign_str(name); - __entry->len = len; memcpy(__get_dynamic_array(data), buf, len); ), TP_printk("%s: len=%u data=%s", - __get_str(name), __entry->len, - __print_hex(__get_dynamic_array(data), __entry->len)) + __get_str(name), __get_dynamic_array_len(data), + __print_hex(__get_dynamic_array(data), + __get_dynamic_array_len(data))) ); DEFINE_EVENT(geni_serial_data, geni_serial_tx_data, diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index 5fbdabe3faea..991fd6f41e4b 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h @@ -547,10 +547,11 @@ TRACE_EVENT_RCU(rcu_segcb_stats, ), TP_fast_assign( + int i; __entry->ctx = ctx; memcpy(__entry->seglen, rs->seglen, RCU_CBLIST_NSEGS * sizeof(long)); - memcpy(__entry->gp_seq, rs->gp_seq, RCU_CBLIST_NSEGS * sizeof(unsigned long)); - + for (i = 0; i < RCU_CBLIST_NSEGS; i++) + __entry->gp_seq[i] = rs->gp_seq[i].norm; ), TP_printk("%s seglen: (DONE=%ld, WAIT=%ld, NEXT_READY=%ld, NEXT=%ld) " @@ -625,7 +626,7 @@ TRACE_EVENT_RCU(rcu_invoke_callback, */ TRACE_EVENT_RCU(rcu_invoke_kvfree_callback, - TP_PROTO(const char *rcuname, struct rcu_head *rhp, unsigned long offset), + TP_PROTO(const char *rcuname, struct kvfree_rcu_head *rhp, unsigned long offset), TP_ARGS(rcuname, rhp, offset), diff --git a/include/trace/events/sched_ext.h b/include/trace/events/sched_ext.h index d1bf5acd59c5..1e54f9564ef3 100644 --- a/include/trace/events/sched_ext.h +++ b/include/trace/events/sched_ext.h @@ -84,6 +84,34 @@ TRACE_EVENT(sched_ext_bypass_lb, ) ); +TRACE_EVENT(sched_ext_exit, + + TP_PROTO(struct scx_sched *sch, __u32 kind), + + TP_ARGS(sch, kind), + + TP_STRUCT__entry( + __string( name, sch->ops.name ) + __field( __s32, level ) + __field( __u64, sub_cgroup_id ) + __string( cgrp_path, sch_cgrp_path(sch) ) + __field( __u32, kind ) + ), + + TP_fast_assign( + __assign_str(name); + __entry->level = sch->level; + __entry->sub_cgroup_id = sch->ops.sub_cgroup_id; + __assign_str(cgrp_path); + __entry->kind = kind; + ), + + TP_printk("sched %s level %d sub_cgroup_id %llu cgrp_path %s kind %u", + __get_str(name), __entry->level, __entry->sub_cgroup_id, + __get_str(cgrp_path), __entry->kind + ) +); + #endif /* _TRACE_SCHED_EXT_H */ /* This part must be outside protection */ diff --git a/include/trace/events/timer.h b/include/trace/events/timer.h index ca82fd62dc30..3aa0608c6361 100644 --- a/include/trace/events/timer.h +++ b/include/trace/events/timer.h @@ -302,7 +302,7 @@ DECLARE_EVENT_CLASS(hrtimer_class, * hrtimer_start_expired - Invoked when a expired timer was started * @hrtimer: pointer to struct hrtimer * - * Preceeded by a hrtimer_start tracepoint. + * Preceded by a hrtimer_start tracepoint. */ DEFINE_EVENT(hrtimer_class, hrtimer_start_expired, diff --git a/include/trace/trace_events.h b/include/trace/trace_events.h index fbc07d353be6..93011f800d0f 100644 --- a/include/trace/trace_events.h +++ b/include/trace/trace_events.h @@ -19,6 +19,7 @@ */ #include <linux/trace_events.h> +#include <linux/btf_ids.h> #ifndef TRACE_SYSTEM_VAR #define TRACE_SYSTEM_VAR TRACE_SYSTEM @@ -397,6 +398,26 @@ static inline notrace int trace_event_get_offsets_##call( \ #define _TRACE_PERF_INIT(call) #endif /* CONFIG_PERF_EVENTS */ +#if defined(CONFIG_BPF_EVENTS) && defined(CONFIG_DEBUG_INFO_BTF) +/* + * Per-template BTF id list, populated at link time by resolve_btfids: + * [0] FUNC __bpf_trace_<call> (the BPF dispatcher) + * [1] STRUCT trace_event_raw_<call> (the ring-buffer record) + * Exposed via the events/<sys>/<name>/btf_ids tracefs file. + */ +#define _TRACE_BTF_IDS_DECLARE(call) \ + BTF_ID_LIST(__bpf_trace_btf_ids_##call) \ + BTF_ID(func, __bpf_trace_##call) \ + BTF_ID(struct, trace_event_raw_##call) + +#define _TRACE_BTF_IDS_INIT(call) \ + .btf_ids = __bpf_trace_btf_ids_##call, + +#else +#define _TRACE_BTF_IDS_DECLARE(call) +#define _TRACE_BTF_IDS_INIT(call) +#endif /* CONFIG_BPF_EVENTS && CONFIG_DEBUG_INFO_BTF */ + #include "stages/stage6_event_callback.h" @@ -474,6 +495,7 @@ static inline void ftrace_test_probe_##call(void) \ #undef DECLARE_EVENT_CLASS #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ _TRACE_PERF_PROTO(call, PARAMS(proto)); \ +_TRACE_BTF_IDS_DECLARE(call) \ static char print_fmt_##call[] = print; \ static struct trace_event_class __used __refdata event_class_##call = { \ .system = TRACE_SYSTEM_STRING, \ @@ -483,6 +505,7 @@ static struct trace_event_class __used __refdata event_class_##call = { \ .probe = trace_event_raw_event_##call, \ .reg = trace_event_reg, \ _TRACE_PERF_INIT(call) \ + _TRACE_BTF_IDS_INIT(call) \ }; #undef DECLARE_EVENT_SYSCALL_CLASS |
