}
 }
 
+static bool is_struct_ops_tramp(const struct bpf_tramp_links *fentry_links)
+{
+       return fentry_links->nr_links == 1 &&
+               fentry_links->links[0]->link.type == BPF_LINK_TYPE_STRUCT_OPS;
+}
+
 /* Based on the x86's implementation of arch_prepare_bpf_trampoline().
  *
  * bpf prog and function entry before bpf trampoline hooked:
        struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN];
        bool save_ret;
        __le32 **branches = NULL;
+       bool is_struct_ops = is_struct_ops_tramp(fentry);
 
        /* trampoline stack layout:
         *                  [ parent ip         ]
         */
        emit_bti(A64_BTI_JC, ctx);
 
-       /* frame for parent function */
-       emit(A64_PUSH(A64_FP, A64_R(9), A64_SP), ctx);
-       emit(A64_MOV(1, A64_FP, A64_SP), ctx);
+       /* x9 is not set for struct_ops */
+       if (!is_struct_ops) {
+               /* frame for parent function */
+               emit(A64_PUSH(A64_FP, A64_R(9), A64_SP), ctx);
+               emit(A64_MOV(1, A64_FP, A64_SP), ctx);
+       }
 
-       /* frame for patched function */
+       /* frame for patched function for tracing, or caller for struct_ops */
        emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx);
        emit(A64_MOV(1, A64_FP, A64_SP), ctx);
 
        /* reset SP  */
        emit(A64_MOV(1, A64_SP, A64_FP), ctx);
 
-       /* pop frames  */
-       emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx);
-       emit(A64_POP(A64_FP, A64_R(9), A64_SP), ctx);
-
-       if (flags & BPF_TRAMP_F_SKIP_FRAME) {
-               /* skip patched function, return to parent */
-               emit(A64_MOV(1, A64_LR, A64_R(9)), ctx);
-               emit(A64_RET(A64_R(9)), ctx);
+       if (is_struct_ops) {
+               emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx);
+               emit(A64_RET(A64_LR), ctx);
        } else {
-               /* return to patched function */
-               emit(A64_MOV(1, A64_R(10), A64_LR), ctx);
-               emit(A64_MOV(1, A64_LR, A64_R(9)), ctx);
-               emit(A64_RET(A64_R(10)), ctx);
+               /* pop frames */
+               emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx);
+               emit(A64_POP(A64_FP, A64_R(9), A64_SP), ctx);
+
+               if (flags & BPF_TRAMP_F_SKIP_FRAME) {
+                       /* skip patched function, return to parent */
+                       emit(A64_MOV(1, A64_LR, A64_R(9)), ctx);
+                       emit(A64_RET(A64_R(9)), ctx);
+               } else {
+                       /* return to patched function */
+                       emit(A64_MOV(1, A64_R(10), A64_LR), ctx);
+                       emit(A64_MOV(1, A64_LR, A64_R(9)), ctx);
+                       emit(A64_RET(A64_R(10)), ctx);
+               }
        }
 
        kfree(branches);