/* Limit how long of an event name plus args within the subsystem. */ #define MAX_EVENT_DESC 512 #define EVENT_NAME(user_event) ((user_event)->reg_name) #define EVENT_TP_NAME(user_event) ((user_event)->tracepoint.name) #define MAX_FIELD_ARRAY_SIZE 1024
/* * Internal bits (kernel side only) to keep track of connected probes: * These are used when status is requested in text form about an event. These * bits are compared against an internal byte on the event to determine which * probes to print out to the user. * * These do not reflect the mapped bytes between the user and kernel space.
*/ #define EVENT_STATUS_FTRACE BIT(0) #define EVENT_STATUS_PERF BIT(1) #define EVENT_STATUS_OTHER BIT(7)
/* * Stores the system name, tables, and locks for a group of events. This * allows isolation for events by various means.
*/ struct user_event_group { char *system_name; char *system_multi_name; struct hlist_node node; struct mutex reg_mutex;
DECLARE_HASHTABLE(register_table, 8); /* ID that moves forward within the group for multi-event names */
u64 multi_id;
};
/* Group for init_user_ns mapping, top-most group */ staticstruct user_event_group *init_group;
/* Max allowed events for the whole system */ staticunsignedint max_user_events = 32768;
/* Current number of events on the whole system */ staticunsignedint current_user_events;
/* * Stores per-event properties, as users register events * within a file a user_event might be created if it does not * already exist. These are globally used and their lifetime * is tied to the refcnt member. These cannot go away until the * refcnt reaches one.
*/ struct user_event { struct user_event_group *group; char *reg_name; struct tracepoint tracepoint; struct trace_event_call call; struct trace_event_class class; struct dyn_event devent; struct hlist_node node; struct list_head fields; struct list_head validators; struct work_struct put_work;
refcount_t refcnt; int min_size; int reg_flags; char status;
};
/* * Stores per-mm/event properties that enable an address to be * updated properly for each task. As tasks are forked, we use * these to track enablement sites that are tied to an event.
*/ struct user_event_enabler { struct list_head mm_enablers_link; struct user_event *event; unsignedlong addr;
/* Track enable bit, flags, etc. Aligned for bitops. */ unsignedlong values;
};
/* Bits 0-5 are for the bit to update upon enable/disable (0-63 allowed) */ #define ENABLE_VAL_BIT_MASK 0x3F
/* Bit 6 is for faulting status of enablement */ #define ENABLE_VAL_FAULTING_BIT 6
/* Bit 7 is for freeing status of enablement */ #define ENABLE_VAL_FREEING_BIT 7
/* Bit 8 is for marking 32-bit on 64-bit */ #define ENABLE_VAL_32_ON_64_BIT 8
/* Used for asynchronous faulting in of pages */ struct user_event_enabler_fault { struct work_struct work; struct user_event_mm *mm; struct user_event_enabler *enabler; int attempt;
};
staticstruct kmem_cache *fault_cache;
/* Global list of memory descriptors using user_events */ static LIST_HEAD(user_event_mms); static DEFINE_SPINLOCK(user_event_mms_lock);
/* * Stores per-file events references, as users register events * within a file this structure is modified and freed via RCU. * The lifetime of this struct is tied to the lifetime of the file. * These are not shared and only accessible by the file that created it.
*/ struct user_event_refs { struct rcu_head rcu; int count; struct user_event *events[];
};
struct user_event_validator { struct list_head user_event_link; int offset; int flags;
};
staticinlinevoid align_addr_bit(unsignedlong *addr, int *bit, unsignedlong *flags)
{ if (IS_ALIGNED(*addr, sizeof(long))) { #ifdef __BIG_ENDIAN /* 32 bit on BE 64 bit requires a 32 bit offset when aligned. */ if (test_bit(ENABLE_VAL_32_ON_64_BIT, flags))
*bit += 32; #endif return;
}
*addr = ALIGN_DOWN(*addr, sizeof(long));
/* * We only support 32 and 64 bit values. The only time we need * to align is a 32 bit value on a 64 bit kernel, which on LE * is always 32 bits, and on BE requires no change when unaligned.
*/ #ifdef __LITTLE_ENDIAN
*bit += 32; #endif
}
if (!refcount_dec_and_test(&user->refcnt)) goto out;
if (destroy_user_event(user)) { /* * The only reason this would fail here is if we cannot * update the visibility of the event. In this case the * event stays in the hashtable, waiting for someone to * attempt to delete it later.
*/
pr_warn("user_events: Unable to delete event\n");
refcount_set(&user->refcnt, 1);
}
out:
mutex_unlock(&event_mutex);
}
/* * When the event is not enabled for auto-delete there will always * be at least 1 reference to the event. During the event creation * we initially set the refcnt to 2 to achieve this. In those cases * the caller must acquire event_mutex and after decrement check if * the refcnt is 1, meaning this is the last reference. When auto * delete is enabled, there will only be 1 ref, IE: refcnt will be * only set to 1 during creation to allow the below checks to go * through upon the last put. The last put must always be done with * the event mutex held.
*/ if (!locked) {
lockdep_assert_not_held(&event_mutex); delete = refcount_dec_and_mutex_lock(&user->refcnt, &event_mutex);
} else {
lockdep_assert_held(&event_mutex); delete = refcount_dec_and_test(&user->refcnt);
}
if (!delete) return;
/* * We now have the event_mutex in all cases, which ensures that * no new references will be taken until event_mutex is released. * New references come through find_user_event(), which requires * the event_mutex to be held.
*/
if (user->reg_flags & USER_EVENT_REG_PERSIST) { /* We should not get here when persist flag is set */
pr_alert("BUG: Auto-delete engaged on persistent event\n"); goto out;
}
/* * Unfortunately we have to attempt the actual destroy in a work * queue. This is because not all cases handle a trace_event_call * being removed within the class->reg() operation for unregister.
*/
INIT_WORK(&user->put_work, delayed_destroy_user_event);
/* * Since the event is still in the hashtable, we have to re-inc * the ref count to 1. This count will be decremented and checked * in the work queue to ensure it's still the last ref. This is * needed because a user-process could register the same event in * between the time of event_mutex release and the work queue * running the delayed destroy. If we removed the item now from * the hashtable, this would result in a timing window where a * user process would fail a register because the trace_event_call * register would fail in the tracing layers.
*/
refcount_set(&user->refcnt, 1);
if (WARN_ON_ONCE(!schedule_work(&user->put_work))) { /* * If we fail we must wait for an admin to attempt delete or * another register/close of the event, whichever is first.
*/
pr_warn("user_events: Unable to queue delayed destroy\n");
}
out: /* Ensure if we didn't have event_mutex before we unlock it */ if (!locked)
mutex_unlock(&event_mutex);
}
/* No longer tracking the event via the enabler */
user_event_put(enabler->event, locked);
kfree(enabler);
}
staticint user_event_mm_fault_in(struct user_event_mm *mm, unsignedlong uaddr, int attempt)
{ bool unlocked; int ret;
/* * Normally this is low, ensure that it cannot be taken advantage of by * bad user processes to cause excessive looping.
*/ if (attempt > 10) return -EFAULT;
mmap_read_lock(mm->mm);
/* Ensure MM has tasks, cannot use after exit_mm() */ if (refcount_read(&mm->tasks) == 0) {
ret = -ENOENT; goto out;
}
ret = fixup_user_fault(mm->mm, uaddr, FAULT_FLAG_WRITE | FAULT_FLAG_REMOTE,
&unlocked);
out:
mmap_read_unlock(mm->mm);
/* Prevent state changes from racing */
mutex_lock(&event_mutex);
/* User asked for enabler to be removed during fault */ if (test_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler))) {
user_event_enabler_destroy(enabler, true); goto out;
}
/* * If we managed to get the page, re-issue the write. We do not * want to get into a possible infinite loop, which is why we only * attempt again directly if the page came in. If we couldn't get * the page here, then we will try again the next time the event is * enabled/disabled.
*/
clear_bit(ENABLE_VAL_FAULTING_BIT, ENABLE_BITOPS(enabler));
/* Update bit atomically, user tracers must be atomic as well */ if (enabler->event && enabler->event->status)
set_bit(bit, ptr); else
clear_bit(bit, ptr);
/* * We need to build a one-shot list of all the mms that have an * enabler for the user_event passed in. This list is only valid * while holding the event_mutex. The only reason for this is due * to the global mm list being RCU protected and we use methods * which can wait (mmap_read_lock and pin_user_pages_remote). * * NOTE: user_event_mm_get_all() increments the ref count of each * mm that is added to the list to prevent removal timing windows. * We must always put each mm after they are used, which may wait.
*/
mm = user_event_mm_get_all(user);
while (mm) {
next = mm->next;
mmap_read_lock(mm->mm);
/* * We use the mm->next field to build a one-shot list from the global * RCU protected list. To build this list the event_mutex must be held. * This lets us build a list without requiring allocs that could fail * when user based events are most wanted for diagnostics.
*/
lockdep_assert_held(&event_mutex);
/* * We do not want to block fork/exec while enablements are being * updated, so we use RCU to walk the current tasks that have used * user_events ABI for 1 or more events. Each enabler found in each * task that matches the event being updated has a write to reflect * the kernel state back into the process. Waits/faults must not occur * during this. So we scan the list under RCU for all the mm that have * the event within it. This is needed because mm_read_lock() can wait. * Each user mm returned has a ref inc to handle remove RCU races.
*/
rcu_read_lock();
/* * The lifetime of the memory descriptor can slightly outlast * the task lifetime if a ref to the user_event_mm is taken * between list_del_rcu() and call_rcu(). Therefore we need * to take a reference to it to ensure it can live this long * under this corner case. This can also occur in clones that * outlast the parent.
*/
mmgrab(user_mm->mm);
/* Clone will increment the tasks, only remove if last clone */ if (!refcount_dec_and_test(&mm->tasks)) return;
/* Remove the mm from the list, so it can no longer be enabled */
spin_lock_irqsave(&user_event_mms_lock, flags);
list_del_rcu(&mm->mms_link);
spin_unlock_irqrestore(&user_event_mms_lock, flags);
/* * We need to wait for currently occurring writes to stop within * the mm. This is required since exit_mm() snaps the current rss * stats and clears them. On the final mmdrop(), check_mm() will * report a bug if these increment. * * All writes/pins are done under mmap_read lock, take the write * lock to ensure in-progress faults have completed. Faults that * are pending but yet to run will check the task count and skip * the fault since the mm is going away.
*/
mmap_write_lock(mm->mm);
mmap_write_unlock(mm->mm);
/* * Put for mm must be done after RCU delay to handle new refs in * between the list_del_rcu() and now. This ensures any get refs * during rcu_read_lock() are accounted for during list removal. * * CPU A | CPU B * --------------------------------------------------------------- * user_event_mm_remove() | rcu_read_lock(); * list_del_rcu() | list_for_each_entry_rcu(); * call_rcu() | refcount_inc(); * . | rcu_read_unlock(); * schedule_work() | . * user_event_mm_put() | . * * mmdrop() cannot be called in the softirq context of call_rcu() * so we use a work queue after call_rcu() to run within.
*/
INIT_RCU_WORK(&mm->put_rwork, delayed_user_event_mm_put);
queue_rcu_work(system_wq, &mm->put_rwork);
}
retry: /* Prevents state changes from racing with new enablers */
mutex_lock(&event_mutex);
/* Attempt to reflect the current state within the process */
mmap_read_lock(user_mm->mm);
*write_result = user_event_enabler_write(user_mm, enabler, false,
&attempt);
mmap_read_unlock(user_mm->mm);
/* * If the write works, then we will track the enabler. A ref to the * underlying user_event is held by the enabler to prevent it going * away while the enabler is still in use by a process. The ref is * removed when the enabler is destroyed. This means a event cannot * be forcefully deleted from the system until all tasks using it * exit or run exec(), which includes forks and clones.
*/ if (!*write_result) {
user_event_get(user);
list_add_rcu(&enabler->mm_enablers_link, &user_mm->enablers);
}
mutex_unlock(&event_mutex);
if (*write_result) { /* Attempt to fault-in and retry if it worked */ if (!user_event_mm_fault_in(user_mm, uaddr, attempt)) goto retry;
/* * Parses a register command for user_events * Format: event_name[:FLAG1[,FLAG2...]] [field1[;field2...]] * * Example event named 'test' with a 20 char 'msg' field with an unsigned int * 'id' field after: * test char[20] msg;unsigned int id * * NOTE: Offsets are from the user data perspective, they are not from the * trace_entry/buffer perspective. We automatically add the common properties * sizes to the offset for the user. * * Upon success user_event has its ref count increased by 1.
*/ staticint user_event_parse_cmd(struct user_event_group *group, char *raw_command, struct user_event **newuser, int reg_flags)
{ char *name = raw_command; char *args = strpbrk(name, " "); char *flags;
if (filter_type == FILTER_OTHER)
field->filter_type = filter_assign_type(type);
list_add(&field->link, &user->fields);
/* * Min size from user writes that are required, this does not include * the size of trace_entry (common fields).
*/
user->min_size = (offset + size) - sizeof(struct trace_entry);
return 0;
}
/* * Parses the values of a field within the description * Format: type name [size]
*/ staticint user_event_parse_field(char *field, struct user_event *user,
u32 *offset)
{ char *part, *type, *name;
u32 depth = 0, saved_offset = *offset; int len, size = -EINVAL; bool is_struct = false;
field = skip_spaces(field);
if (*field == '\0') return 0;
/* Handle types that have a space within */
len = str_has_prefix(field, "unsigned "); if (len) goto skip_next;
len = str_has_prefix(field, "struct "); if (len) {
is_struct = true; goto skip_next;
}
len = str_has_prefix(field, "__data_loc unsigned "); if (len) goto skip_next;
len = str_has_prefix(field, "__data_loc "); if (len) goto skip_next;
len = str_has_prefix(field, "__rel_loc unsigned "); if (len) goto skip_next;
len = str_has_prefix(field, "__rel_loc "); if (len) goto skip_next;
goto parse;
skip_next:
type = field;
field = strpbrk(field + len, " ");
if (field == NULL) return -EINVAL;
*field++ = '\0';
depth++;
parse:
name = NULL;
while ((part = strsep(&field, " ")) != NULL) { switch (depth++) { case FIELD_DEPTH_TYPE:
type = part; break; case FIELD_DEPTH_NAME:
name = part; break; case FIELD_DEPTH_SIZE: if (!is_struct) return -EINVAL;
/* * While by default tracefs is locked down, systems can be configured * to allow user_event files to be less locked down. The extreme case * being "other" has read/write access to user_events_data/status. * * When not locked down, processes may not have permissions to * add/remove calls themselves to tracefs. We need to temporarily * switch to root file permission to allow for this scenario.
*/
cred->fsuid = GLOBAL_ROOT_UID;
old_cred = override_creds(cred);
if (visible)
ret = trace_add_event_call(&user->call); else
ret = trace_remove_event_call(&user->call);
revert_creds(old_cred);
put_cred(cred);
return ret;
}
staticint destroy_user_event(struct user_event *user)
{ int ret = 0;
lockdep_assert_held(&event_mutex);
/* Must destroy fields before call removal */
user_event_destroy_fields(user);
hash_for_each_possible(group->register_table, user, node, key) { /* * Single-format events shouldn't return multi-format * events. Callers expect the underlying tracepoint to match * the name exactly in these cases. Only check like-formats.
*/ if (EVENT_MULTI_FORMAT(flags) != EVENT_MULTI_FORMAT(user->reg_flags)) continue;
if (strcmp(EVENT_NAME(user), name)) continue;
if (user_fields_match(user, argc, argv)) return user_event_get(user);
/* Scan others if this is a multi-format event */ if (EVENT_MULTI_FORMAT(flags)) continue;
/* * Update the enabled bit among all user processes.
*/ staticvoid update_enable_bit_for(struct user_event *user)
{ struct tracepoint *tp = &user->tracepoint; char status = 0;
if (static_key_enabled(&tp->key)) { struct tracepoint_func *probe_func_ptr;
user_event_func_t probe_func;
if (probe_func_ptr) { do {
probe_func = probe_func_ptr->func;
if (probe_func == user_event_ftrace)
status |= EVENT_STATUS_FTRACE; #ifdef CONFIG_PERF_EVENTS elseif (probe_func == user_event_perf)
status |= EVENT_STATUS_PERF; #endif else
status |= EVENT_STATUS_OTHER;
} while ((++probe_func_ptr)->func);
}
rcu_read_unlock_sched();
}
user->status = status;
user_event_enabler_update(user);
}
/* * Register callback for our events from tracing sub-systems.
*/ staticint user_event_reg(struct trace_event_call *call, enum trace_reg type, void *data)
{ struct user_event *user = (struct user_event *)call->data; int ret = 0;
if (!user) return -ENOENT;
switch (type) { case TRACE_REG_REGISTER:
ret = tracepoint_probe_register(call->tp,
call->class->probe,
data); if (!ret) goto inc; break;
case TRACE_REG_UNREGISTER:
tracepoint_probe_unregister(call->tp,
call->class->probe,
data); goto dec;
#ifdef CONFIG_PERF_EVENTS case TRACE_REG_PERF_REGISTER:
ret = tracepoint_probe_register(call->tp,
call->class->perf_probe,
data); if (!ret) goto inc; break;
case TRACE_REG_PERF_UNREGISTER:
tracepoint_probe_unregister(call->tp,
call->class->perf_probe,
data); goto dec;
case TRACE_REG_PERF_OPEN: case TRACE_REG_PERF_CLOSE: case TRACE_REG_PERF_ADD: case TRACE_REG_PERF_DEL: break; #endif
}
/* Inc to ensure unique multi-event name next time */
user->group->multi_id++;
} else { /* Non Multi-format uses register name */
user->call.name = user->reg_name;
user->tracepoint.name = user->reg_name;
}
return 0;
}
/* * Counts how many ';' without a trailing space are in the args.
*/ staticint count_semis_no_space(char *args)
{ int count = 0;
while ((args = strchr(args, ';'))) {
args++;
if (!isspace(*args))
count++;
}
return count;
}
/* * Copies the arguments while ensuring all ';' have a trailing space.
*/ staticchar *insert_space_after_semis(char *args, int count)
{ char *fixed, *pos; int len;
staticchar **user_event_argv_split(char *args, int *argc)
{ char **split; char *fixed; int count;
/* Count how many ';' without a trailing space */
count = count_semis_no_space(args);
/* No fixup is required */ if (!count) return argv_split(GFP_KERNEL, args, argc);
/* We must fixup 'field;field' to 'field; field' */
fixed = insert_space_after_semis(args, count);
if (!fixed) return NULL;
/* We do a normal split afterwards */
split = argv_split(GFP_KERNEL, fixed, argc);
/* We can free since argv_split makes a copy */
kfree(fixed);
return split;
}
/* * Parses the event name, arguments and flags then registers if successful. * The name buffer lifetime is owned by this method for success cases only. * Upon success the returned user_event has its ref count increased by 1.
*/ staticint user_event_parse(struct user_event_group *group, char *name, char *args, char *flags, struct user_event **newuser, int reg_flags)
{ struct user_event *user; char **argv = NULL; int argc = 0; int ret;
u32 key;
/* Currently don't support any text based flags */ if (flags != NULL) return -EINVAL;
if (!user_event_capable(reg_flags)) return -EPERM;
if (args) {
argv = user_event_argv_split(args, &argc);
if (!argv) return -ENOMEM;
}
/* Prevent dyn_event from racing */
mutex_lock(&event_mutex);
user = find_user_event(group, name, argc, (constchar **)argv,
reg_flags, &key);
mutex_unlock(&event_mutex);
if (argv)
argv_free(argv);
if (IS_ERR(user)) return PTR_ERR(user);
if (user) {
*newuser = user; /* * Name is allocated by caller, free it since it already exists. * Caller only worries about failure cases for freeing.
*/
kfree(name);
return 0;
}
user = kzalloc(sizeof(*user), GFP_KERNEL_ACCOUNT);
/* Caller frees reg_name on error, but not multi-name */ if (EVENT_NAME(user) != EVENT_TP_NAME(user))
kfree(EVENT_TP_NAME(user));
kfree(user); return ret;
}
/* * Deletes previously created events if they are no longer being used.
*/ staticint delete_user_event(struct user_event_group *group, char *name)
{ struct user_event *user; struct hlist_node *tmp;
u32 key = user_event_key(name); int ret = -ENOENT;
/* Attempt to delete all event(s) with the name passed in */
hash_for_each_possible_safe(group->register_table, user, tmp, node, key) { if (strcmp(EVENT_NAME(user), name)) continue;
if (!user_event_last_ref(user)) return -EBUSY;
if (!user_event_capable(user->reg_flags)) return -EPERM;
ret = destroy_user_event(user);
if (ret) goto out;
}
out: return ret;
}
/* * Validates the user payload and writes via iterator.
*/ static ssize_t user_events_write_core(struct file *file, struct iov_iter *i)
{ struct user_event_file_info *info = file->private_data; struct user_event_refs *refs; struct user_event *user = NULL; struct tracepoint *tp;
ssize_t ret = i->count; int idx;
if (unlikely(copy_from_iter(&idx, sizeof(idx), i) != sizeof(idx))) return -EFAULT;
if (idx < 0) return -EINVAL;
rcu_read_lock_sched();
refs = rcu_dereference_sched(info->refs);
/* * The refs->events array is protected by RCU, and new items may be * added. But the user retrieved from indexing into the events array * shall be immutable while the file is opened.
*/ if (likely(refs && idx < refs->count))
user = refs->events[idx];
rcu_read_unlock_sched();
if (unlikely(user == NULL)) return -ENOENT;
if (unlikely(i->count < user->min_size)) return -EINVAL;
tp = &user->tracepoint;
/* * It's possible key.enabled disables after this check, however * we don't mind if a few events are included in this condition.
*/ if (likely(static_key_enabled(&tp->key))) { struct tracepoint_func *probe_func_ptr;
user_event_func_t probe_func; struct iov_iter copy; void *tpdata; bool faulted;
if (unlikely(fault_in_iov_iter_readable(i, i->count))) return -EFAULT;
/* * Registers a user_event on behalf of a user process.
*/ staticlong user_events_ioctl_reg(struct user_event_file_info *info, unsignedlong uarg)
{ struct user_reg __user *ureg = (struct user_reg __user *)uarg; struct user_reg reg; struct user_event *user; struct user_event_enabler *enabler; char *name; long ret; int write_result;
ret = user_reg_get(ureg, ®);
if (ret) return ret;
/* * Prevent users from using the same address and bit multiple times * within the same mm address space. This can cause unexpected behavior * for user processes that is far easier to debug if this is explictly * an error upon registering.
*/ if (current_user_event_enabler_exists((unsignedlong)reg.enable_addr,
reg.enable_bit)) return -EADDRINUSE;
name = strndup_user((constchar __user *)(uintptr_t)reg.name_args,
MAX_EVENT_DESC);
if (IS_ERR(name)) {
ret = PTR_ERR(name); return ret;
}
ret = user_event_parse_cmd(info->group, name, &user, reg.flags);
if (ret) {
kfree(name); return ret;
}
ret = user_events_ref_add(info, user);
/* No longer need parse ref, ref_add either worked or not */
user_event_put(user, false);
/* Positive number is index and valid */ if (ret < 0) return ret;
/* * user_events_ref_add succeeded: * At this point we have a user_event, it's lifetime is bound by the * reference count, not this file. If anything fails, the user_event * still has a reference until the file is released. During release * any remaining references (from user_events_ref_add) are decremented. * * Attempt to create an enabler, which too has a lifetime tied in the * same way for the event. Once the task that caused the enabler to be * created exits or issues exec() then the enablers it has created * will be destroyed and the ref to the event will be decremented.
*/
enabler = user_event_enabler_create(®, user, &write_result);
if (!enabler) return -ENOMEM;
/* Write failed/faulted, give error back to caller */ if (write_result) return write_result;
put_user((u32)ret, &ureg->write_index);
return 0;
}
/* * Deletes a user_event on behalf of a user process.
*/ staticlong user_events_ioctl_del(struct user_event_file_info *info, unsignedlong uarg)
{ void __user *ubuf = (void __user *)uarg; char *name; long ret;
name = strndup_user(ubuf, MAX_EVENT_DESC);
if (IS_ERR(name)) return PTR_ERR(name);
/* event_mutex prevents dyn_event from racing */
mutex_lock(&event_mutex);
ret = delete_user_event(info->group, name);
mutex_unlock(&event_mutex);
if (size < offsetofend(struct user_unreg, disable_addr)) return -EINVAL;
ret = copy_struct_from_user(kreg, sizeof(*kreg), ureg, size);
/* Ensure no reserved values, since we don't support any yet */ if (kreg->__reserved || kreg->__reserved2) return -EINVAL;
return ret;
}
staticint user_event_mm_clear_bit(struct user_event_mm *user_mm, unsignedlong uaddr, unsignedchar bit, unsignedlong flags)
{ struct user_event_enabler enabler; int result; int attempt = 0;
memset(&enabler, 0, sizeof(enabler));
enabler.addr = uaddr;
enabler.values = bit | flags;
retry: /* Prevents state changes from racing with new enablers */
mutex_lock(&event_mutex);
/* Force the bit to be cleared, since no event is attached */
mmap_read_lock(user_mm->mm);
result = user_event_enabler_write(user_mm, &enabler, false, &attempt);
mmap_read_unlock(user_mm->mm);
mutex_unlock(&event_mutex);
if (result) { /* Attempt to fault-in and retry if it worked */ if (!user_event_mm_fault_in(user_mm, uaddr, attempt)) goto retry;
}
return result;
}
/* * Unregisters an enablement address/bit within a task/user mm.
*/ staticlong user_events_ioctl_unreg(unsignedlong uarg)
{ struct user_unreg __user *ureg = (struct user_unreg __user *)uarg; struct user_event_mm *mm = current->user_event_mm; struct user_event_enabler *enabler, *next; struct user_unreg reg; unsignedlong flags; long ret;
ret = user_unreg_get(ureg, ®);
if (ret) return ret;
if (!mm) return -ENOENT;
flags = 0;
ret = -ENOENT;
/* * Flags freeing and faulting are used to indicate if the enabler is in * use at all. When faulting is set a page-fault is occurring asyncly. * During async fault if freeing is set, the enabler will be destroyed. * If no async fault is happening, we can destroy it now since we hold * the event_mutex during these checks.
*/
mutex_lock(&event_mutex);
/* We must keep compat flags for the clear */
flags |= enabler->values & ENABLE_VAL_COMPAT_MASK;
if (!test_bit(ENABLE_VAL_FAULTING_BIT, ENABLE_BITOPS(enabler)))
user_event_enabler_destroy(enabler, true);
/* Removed at least one */
ret = 0;
}
}
mutex_unlock(&event_mutex);
/* Ensure bit is now cleared for user, regardless of event status */ if (!ret)
ret = user_event_mm_clear_bit(mm, reg.disable_addr,
reg.disable_bit, flags);
return ret;
}
/* * Handles the ioctl from user mode to register or alter operations.
*/ staticlong user_events_ioctl(struct file *file, unsignedint cmd, unsignedlong uarg)
{ struct user_event_file_info *info = file->private_data; struct user_event_group *group = info->group; long ret = -ENOTTY;
switch (cmd) { case DIAG_IOCSREG:
mutex_lock(&group->reg_mutex);
ret = user_events_ioctl_reg(info, uarg);
mutex_unlock(&group->reg_mutex); break;
case DIAG_IOCSDEL:
mutex_lock(&group->reg_mutex);
ret = user_events_ioctl_del(info, uarg);
mutex_unlock(&group->reg_mutex); break;
case DIAG_IOCSUNREG:
mutex_lock(&group->reg_mutex);
ret = user_events_ioctl_unreg(uarg);
mutex_unlock(&group->reg_mutex); break;
}
return ret;
}
/* * Handles the final close of the file from user mode.
*/ staticint user_events_release(struct inode *node, struct file *file)
{ struct user_event_file_info *info = file->private_data; struct user_event_group *group; struct user_event_refs *refs; int i;
if (!info) return -EINVAL;
group = info->group;
/* * Ensure refs cannot change under any situation by taking the * register mutex during the final freeing of the references.
*/
mutex_lock(&group->reg_mutex);
refs = info->refs;
if (!refs) goto out;
/* * The lifetime of refs has reached an end, it's tied to this file. * The underlying user_events are ref counted, and cannot be freed. * After this decrement, the user_events may be freed elsewhere.
*/ for (i = 0; i < refs->count; ++i)
user_event_put(refs->events[i], false);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.