/* * The SRCU here makes sure that STM device doesn't disappear from under a * stm_source_write() caller, which may want to have as little overhead as * possible.
*/ staticstruct srcu_struct stm_source_srcu;
/** * stm_find_device() - find stm device by name * @buf: character buffer containing the name * * This is called when either policy gets assigned to an stm device or an * stm_source device gets linked to an stm device. * * This grabs device's reference (get_device()) and module reference, both * of which the calling path needs to make sure to drop with stm_put_device(). * * Return: stm device pointer or null if lookup failed.
*/ struct stm_device *stm_find_device(constchar *buf)
{ struct stm_device *stm; struct device *dev;
if (!stm_core_up) return NULL;
dev = class_find_device_by_name(&stm_class, buf); if (!dev) return NULL;
/** * stm_put_device() - drop references on the stm device * @stm: stm device, previously acquired by stm_find_device() * * This drops the module reference and device reference taken by * stm_find_device() or stm_char_open().
*/ void stm_put_device(struct stm_device *stm)
{
module_put(stm->owner);
put_device(&stm->dev);
}
/* * Internally we only care about software-writable masters here, that is the * ones in the range [stm_data->sw_start..stm_data..sw_end], however we need * original master numbers to be visible externally, since they are the ones * that will appear in the STP stream. Thus, the internal bookkeeping uses * $master - stm_data->sw_start to reference master descriptors and such.
*/
/* * This is like bitmap_find_free_region(), except it can ignore @start bits * at the beginning.
*/ staticint find_free_channels(unsignedlong *bitmap, unsignedint start, unsignedint end, unsignedint width)
{ unsignedint pos; int i;
for (pos = start; pos < end + 1; pos = ALIGN(pos, width)) {
pos = find_next_zero_bit(bitmap, end + 1, pos); if (pos + width > end + 1) break;
if (pos & (width - 1)) continue;
for (i = 1; i < width && !test_bit(pos + i, bitmap); i++)
; if (i == width) return pos;
/* step over [pos..pos+i) to continue search */
pos += i;
}
if (width > stm->data->sw_nchannels) return -EINVAL;
/* We no longer accept policy_node==NULL here */ if (WARN_ON_ONCE(!policy_node)) return -EINVAL;
/* * Also, the caller holds reference to policy_node, so it won't * disappear on us.
*/
stp_policy_node_get_ranges(policy_node, &midx, &mend, &cidx, &cend);
spin_lock(&stm->mc_lock);
spin_lock(&output->lock); /* output is already assigned -- shouldn't happen */ if (WARN_ON_ONCE(output->nr_chans)) goto unlock;
ret = stm_find_master_chan(stm, width, &midx, mend, &cidx, cend); if (ret < 0) goto unlock;
/* * Framing protocol management * Modules can implement STM protocol drivers and (un-)register them * with the STM class framework.
*/ staticstruct list_head stm_pdrv_head; staticstruct mutex stm_pdrv_mutex;
/* * On success, stp_policy_node_lookup() will return holding the * configfs subsystem mutex, which is then released in * stp_policy_node_put(). This allows the pdrv->output_open() in * stm_output_assign() to serialize against the attribute accessors.
*/ for (n = 0, pn = NULL; ids[n] && !pn; n++)
pn = stp_policy_node_lookup(stm, ids[n]);
if (!pn) return -EINVAL;
err = stm_output_assign(stm, width, pn, output);
stp_policy_node_put(pn);
return err;
}
/** * stm_data_write() - send the given payload as data packets * @data: stm driver's data * @m: STP master * @c: STP channel * @ts_first: timestamp the first packet * @buf: data payload buffer * @count: data payload size
*/
ssize_t notrace stm_data_write(struct stm_data *data, unsignedint m, unsignedint c, bool ts_first, constvoid *buf,
size_t count)
{ unsignedint flags = ts_first ? STP_PACKET_TIMESTAMPED : 0;
ssize_t sz;
size_t pos;
/* * If no m/c have been assigned to this writer up to this * point, try to use the task name and "default" policy entries.
*/ if (!stmf->output.nr_chans) { char comm[sizeof(current->comm)]; char *ids[] = { comm, "default", NULL };
get_task_comm(comm, current);
err = stm_assign_first_policy(stmf->stm, &stmf->output, ids, 1); /* * EBUSY means that somebody else just assigned this * output, which is just fine for write()
*/ if (err) return err;
}
kbuf = kmalloc(count + 1, GFP_KERNEL); if (!kbuf) return -ENOMEM;
/* * size + 1 to make sure the .id string at the bottom is terminated, * which is also why memdup_user() is not useful here
*/
id = kzalloc(size + 1, GFP_KERNEL); if (!id) return -ENOMEM;
if (copy_from_user(id, arg, size)) {
ret = -EFAULT; goto err_free;
}
if (id->__reserved_0 || id->__reserved_1) goto err_free;
if (stm->data->sw_mmiosz)
wlimit = PAGE_SIZE / stm->data->sw_mmiosz;
if (id->width < 1 || id->width > wlimit) goto err_free;
ids[0] = id->id;
ret = stm_assign_first_policy(stmf->stm, &stmf->output, ids,
id->width); if (ret) goto err_free;
if (stm->data->link)
ret = stm->data->link(stm->data, stmf->output.master,
stmf->output.channel);
if (ret)
stm_output_free(stmf->stm, &stmf->output);
/* initialize the object before it is accessible via sysfs */
spin_lock_init(&stm->mc_lock);
mutex_init(&stm->policy_mutex);
stm->sw_nmasters = nmasters;
stm->owner = owner;
stm->data = stm_data;
stm_data->stm = stm;
err = kobject_set_name(&stm->dev.kobj, "%s", stm_data->name); if (err) goto err_device;
err = device_add(&stm->dev); if (err) goto err_device;
/* * Use delayed autosuspend to avoid bouncing back and forth * on recurring character device writes, with the initial * delay time of 2 seconds.
*/
pm_runtime_no_callbacks(&stm->dev);
pm_runtime_use_autosuspend(&stm->dev);
pm_runtime_set_autosuspend_delay(&stm->dev, 2000);
pm_runtime_set_suspended(&stm->dev);
pm_runtime_enable(&stm->dev);
mutex_lock(&stm->link_mutex);
list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) {
ret = __stm_source_link_drop(src, stm); /* * src <-> stm link must not change under the same * stm::link_mutex, so complain loudly if it has; * also in this situation ret!=0 means this src is * not connected to this stm and it should be otherwise * safe to proceed with the tear-down of stm.
*/
WARN_ON_ONCE(ret);
}
mutex_unlock(&stm->link_mutex);
synchronize_srcu(&stm_source_srcu);
unregister_chrdev(stm->major, stm_data->name);
mutex_lock(&stm->policy_mutex); if (stm->policy)
stp_policy_unbind(stm->policy);
mutex_unlock(&stm->policy_mutex);
for (i = stm->data->sw_start; i <= stm->data->sw_end; i++)
stp_master_free(stm, i);
/* * stm::link_list access serialization uses a spinlock and a mutex; holding * either of them guarantees that the list is stable; modification requires * holding both of them. * * Lock ordering is as follows: * stm::link_mutex * stm::link_lock * src::link_lock
*/
/** * stm_source_link_add() - connect an stm_source device to an stm device * @src: stm_source device * @stm: stm device * * This function establishes a link from stm_source to an stm device so that * the former can send out trace data to the latter. * * Return: 0 on success, -errno otherwise.
*/ staticint stm_source_link_add(struct stm_source_device *src, struct stm_device *stm)
{ char *ids[] = { NULL, "default", NULL }; int err = -ENOMEM;
/* src->link is dereferenced under stm_source_srcu but not the list */
rcu_assign_pointer(src->link, stm);
list_add_tail(&src->link_entry, &stm->link_list);
/* this is to notify the STM device that a new link has been made */ if (stm->data->link)
err = stm->data->link(stm->data, src->output.master,
src->output.channel);
if (err) goto fail_free_output;
/* this is to let the source carry out all necessary preparations */ if (src->data->link)
src->data->link(src->data);
/** * __stm_source_link_drop() - detach stm_source from an stm device * @src: stm_source device * @stm: stm device * * If @stm is @src::link, disconnect them from one another and put the * reference on the @stm device. * * Caller must hold stm::link_mutex.
*/ staticint __stm_source_link_drop(struct stm_source_device *src, struct stm_device *stm)
{ struct stm_device *link; int ret = 0;
lockdep_assert_held(&stm->link_mutex);
/* for stm::link_list modification, we hold both mutex and spinlock */
spin_lock(&stm->link_lock);
spin_lock(&src->link_lock);
link = srcu_dereference_check(src->link, &stm_source_srcu, 1);
/* * The linked device may have changed since we last looked, because * we weren't holding the src::link_lock back then; if this is the * case, tell the caller to retry.
*/ if (link != stm) {
ret = -EAGAIN; goto unlock;
}
/* * Call the unlink callbacks for both source and stm, when we know * that we have actually performed the unlinking.
*/ if (!ret) { if (src->data->unlink)
src->data->unlink(src->data);
if (stm->data->unlink)
stm->data->unlink(stm->data, src->output.master,
src->output.channel);
}
return ret;
}
/** * stm_source_link_drop() - detach stm_source from its stm device * @src: stm_source device * * Unlinking means disconnecting from source's STM device; after this * writes will be unsuccessful until it is linked to a new STM device. * * This will happen on "stm_source_link" sysfs attribute write to undo * the existing link (if any), or on linked STM device's de-registration.
*/ staticvoid stm_source_link_drop(struct stm_source_device *src)
{ struct stm_device *stm; int idx, ret;
retry:
idx = srcu_read_lock(&stm_source_srcu); /* * The stm device will be valid for the duration of this * read section, but the link may change before we grab * the src::link_lock in __stm_source_link_drop().
*/
stm = srcu_dereference(src->link, &stm_source_srcu);
ret = 0; if (stm) {
mutex_lock(&stm->link_mutex);
ret = __stm_source_link_drop(src, stm);
mutex_unlock(&stm->link_mutex);
}
srcu_read_unlock(&stm_source_srcu, idx);
/* if it did change, retry */ if (ret == -EAGAIN) goto retry;
}
/** * stm_source_register_device() - register an stm_source device * @parent: parent device * @data: device description structure * * This will create a device of stm_source class that can write * data to an stm device once linked. * * Return: 0 on success, -errno otherwise.
*/ int stm_source_register_device(struct device *parent, struct stm_source_data *data)
{ struct stm_source_device *src; int err;
if (!stm_core_up) return -EPROBE_DEFER;
src = kzalloc(sizeof(*src), GFP_KERNEL); if (!src) return -ENOMEM;
/** * stm_source_unregister_device() - unregister an stm_source device * @data: device description that was used to register the device * * This will remove a previously created stm_source device from the system.
*/ void stm_source_unregister_device(struct stm_source_data *data)
{ struct stm_source_device *src = data->src;
/* * So as to not confuse existing users with a requirement * to load yet another module, do it here.
*/ if (IS_ENABLED(CONFIG_STM_PROTO_BASIC))
(void)request_module_nowait("stm_p_basic");
stm_core_up++;
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.