The assignment is an example of using Compund Literals. According to C99 Section #6.5.2.5:
A postfix expression that consists of a parenthesized type name
followed by a brace- enclosed list of initializers is a compound
literal. It provides an unnamed object whose value is given by the
initializer list.
In simpler version, according to GCC docs: Compound literals:
A compound literal looks like a cast of a brace-enclosed aggregate
initializer list. Its value is an object of the type specified in the
cast, containing the elements specified in the initializer. Unlike the
result of a cast, a compound literal is an lvalue. ISO C99 and later
support compound literals. As an extension, GCC supports compound
literals also in C90 mode and in C++, although as explained below, the
C++ semantics are somewhat different.
An simple example:
struct foo { int x; int y; };
func() {
struct foo var = { .x = 2, .y = 3 };
...
}
In the question's example, the struct file_operations is defined in include/linux/fs.h and tracing_fops is in kernel/trace/trace.c file in Linux source tree.
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
...
};
The open, read, write are Function Pointers which are pointers that points to a function. After dereferencing the function pointer, it can be used as normal function call. The tracing_fops structure is file_operations type. The values of function pointer members are assigned to the functions in the same trace.c file using compound literals.
With compound literals, we don't have to explicitly specify/assign all members in the structure type because other members are set to zero or null. Structure objects created using compound literals can be passed to functions without depending on member order. The function parameters should be same for both side. For example, the parameters of
int (*open) (struct inode *, struct file *);
is same as
int tracing_open(struct inode *inode, struct file *file);
In object oriented programming, this idea is somewhat similar as Virtual Function Table.