/* * Send acknowledgement to OPAL
*/ static int64_t dump_send_ack(uint32_t dump_id)
{ int rc;
rc = opal_dump_ack(dump_id); if (rc)
pr_warn("%s: Failed to send ack to Dump ID 0x%x (%d)\n",
__func__, dump_id, rc); return rc;
}
static ssize_t dump_ack_store(struct dump_obj *dump_obj, struct dump_attribute *attr, constchar *buf,
size_t count)
{ /* * Try to self remove this attribute. If we are successful, * delete the kobject itself.
*/ if (sysfs_remove_file_self(&dump_obj->kobj, &attr->attr)) {
dump_send_ack(dump_obj->id);
kobject_put(&dump_obj->kobj);
} return count;
}
/* Attributes of a dump * The binary attribute of the dump itself is dynamic * due to the dynamic size of the dump
*/ staticstruct dump_attribute id_attribute =
__ATTR(id, 0444, dump_id_show, NULL); staticstruct dump_attribute type_attribute =
__ATTR(type, 0444, dump_type_show, NULL); staticstruct dump_attribute ack_attribute =
__ATTR(acknowledge, 0660, dump_ack_show, dump_ack_store);
return -EIO;
} if (rc == OPAL_PARTIAL) { /* On a partial read, we just return EIO * and rely on userspace to ask us to try * again.
*/
pr_info("%s: Platform dump partially read. ID = 0x%x\n",
__func__, dump->id); return -EIO;
}
}
memcpy(buffer, dump->buffer + pos, count);
/* You may think we could free the dump buffer now and retrieve * it again later if needed, but due to current firmware limitation, * that's not the case. So, once read into userspace once, * we keep the dump around until it's acknowledged by userspace.
*/
/* * As soon as the sysfs file for this dump is created/activated there is * a chance the opal_errd daemon (or any userspace) might read and * acknowledge the dump before kobject_uevent() is called. If that * happens then there is a potential race between * dump_ack_store->kobject_put() and kobject_uevent() which leads to a * use-after-free of a kernfs object resulting in a kernel crash. * * To avoid that, we need to take a reference on behalf of the bin file, * so that our reference remains valid while we call kobject_uevent(). * We then drop our reference before exiting the function, leaving the * bin file to drop the last reference (if it hasn't already).
*/
/* Take a reference for the bin file */
kobject_get(&dump->kobj);
rc = sysfs_create_bin_file(&dump->kobj, &dump->dump_attr); if (rc == 0) {
kobject_uevent(&dump->kobj, KOBJ_ADD);
pr_info("%s: New platform dump. ID = 0x%x Size %u\n",
__func__, dump->id, dump->size);
} else { /* Drop reference count taken for bin file */
kobject_put(&dump->kobj);
}
/* Drop our reference */
kobject_put(&dump->kobj); return;
}
/* we may get notified twice, let's handle * that gracefully and not create two conflicting * entries.
*/
kobj = kset_find_obj(dump_kset, name); if (kobj) { /* Drop reference added by kset_find_obj() */
kobject_put(kobj); return IRQ_HANDLED;
}
create_dump_obj(dump_id, dump_size, dump_type);
return IRQ_HANDLED;
}
void __init opal_platform_dump_init(void)
{ int rc; int dump_irq;
/* Dump not supported by firmware */ if (!opal_check_token(OPAL_DUMP_READ)) return;
dump_kset = kset_create_and_add("dump", NULL, opal_kobj); if (!dump_kset) {
pr_warn("%s: Failed to create dump kset\n", __func__); return;
}
rc = sysfs_create_group(&dump_kset->kobj, &initiate_attr_group); if (rc) {
pr_warn("%s: Failed to create initiate dump attr group\n",
__func__);
kobject_put(&dump_kset->kobj); return;
}
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.