Changeset View
Standalone View
sys/fs/tmpfs/tmpfs_subr.c
Show First 20 Lines • Show All 246 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
int | int | ||||
tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *tmp, enum vtype type, | tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *tmp, enum vtype type, | ||||
uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *parent, | uid_t uid, gid_t gid, mode_t mode, struct tmpfs_node *parent, | ||||
const char *target, dev_t rdev, struct tmpfs_node **node) | const char *target, dev_t rdev, struct tmpfs_node **node) | ||||
{ | { | ||||
struct tmpfs_node *nnode; | struct tmpfs_node *nnode; | ||||
vm_object_t obj; | vm_object_t obj; | ||||
char *symlink; | |||||
bool symlink_smr; | |||||
/* If the root directory of the 'tmp' file system is not yet | /* If the root directory of the 'tmp' file system is not yet | ||||
* allocated, this must be the request to do it. */ | * allocated, this must be the request to do it. */ | ||||
MPASS(IMPLIES(tmp->tm_root == NULL, parent == NULL && type == VDIR)); | MPASS(IMPLIES(tmp->tm_root == NULL, parent == NULL && type == VDIR)); | ||||
MPASS(IFF(type == VLNK, target != NULL)); | MPASS(IFF(type == VLNK, target != NULL)); | ||||
MPASS(IFF(type == VBLK || type == VCHR, rdev != VNOVAL)); | MPASS(IFF(type == VBLK || type == VCHR, rdev != VNOVAL)); | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *tmp, enum vtype type, | ||||
case VFIFO: | case VFIFO: | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
case VSOCK: | case VSOCK: | ||||
break; | break; | ||||
case VLNK: | case VLNK: | ||||
MPASS(strlen(target) < MAXPATHLEN); | MPASS(strlen(target) < MAXPATHLEN); | ||||
nnode->tn_size = strlen(target); | nnode->tn_size = strlen(target); | ||||
nnode->tn_link = malloc(nnode->tn_size, M_TMPFSNAME, | |||||
M_WAITOK); | symlink = NULL; | ||||
memcpy(nnode->tn_link, target, nnode->tn_size); | if (!tmp->tm_nonc) { | ||||
symlink = cache_symlink_alloc(nnode->tn_size + 1, | |||||
M_WAITOK | M_ZERO); | |||||
symlink_smr = true; | |||||
} | |||||
if (symlink == NULL) { | |||||
symlink = malloc(nnode->tn_size + 1, M_TMPFSNAME, | |||||
M_WAITOK | M_ZERO); | |||||
symlink_smr = false; | |||||
} | |||||
memcpy(symlink, target, nnode->tn_size); | |||||
/* | |||||
kib: This store is release to ensure that symlink_smr update is visible if tn_link_target is non… | |||||
Done Inline ActionsNo, it is to ensure the content of the buffer itself is fully populated before the pointer is set. The read side gets away with data dependency barrier (or nothing on all supported architectures). tn_link_smr starts as false and in absolutely worst case we can get a false negative, which is harmless. however, perhaps all tn_link_smr accesses should be gated behind atomic_* as well. mjg: No, it is to ensure the content of the buffer itself is fully populated before the pointer is… | |||||
* Allow safe symlink resolving for lockless lookup. | |||||
* tmpfs_fplookup_symlink references this comment. | |||||
* | |||||
* 1. nnode is not yet visible to the world. | |||||
* 2. store release of tn_link_smr guarantees that tn_link_target | |||||
Not Done Inline Actions'publishes publishes' kib: 'publishes publishes' | |||||
* is set and the buffer it points to fully populated | |||||
* 3. tn_link_target content is immutable until node destruction, | |||||
* where the pointer gets set to NULL | |||||
* 4. tn_link_smr is never changed once set | |||||
* | |||||
* As a result anyone obtaining nnode pointer past this point | |||||
* and issuing a data dependency barrier is is guaranteed to | |||||
* always see either the correct buffer or NULL. The | |||||
* tn_link_smr flag may be set to true despite being stale, but | |||||
* it will never be set incorrectly if the buffer is present. | |||||
* | |||||
* Since data dependency barrier is a nop on all supported | |||||
* architectures, consumers just load the pointer. | |||||
*/ | |||||
atomic_store_ptr((uintptr_t *)&nnode->tn_link_target, | |||||
(uintptr_t)symlink); | |||||
atomic_store_rel_char((char *)&nnode->tn_link_smr, symlink_smr); | |||||
Not Done Inline ActionsThere is no 'data dep barrier' in the freebsd atomic(9) memory model. Also, and perhaps more important, store_rel() does not work unless load is load_acq(). You need to ensure and explain why symlink != NULL, symlink non-smr and symlink_smr == true is impossible to observe. kib: There is no 'data dep barrier' in the freebsd atomic(9) memory model. Also, and perhaps more… | |||||
Done Inline ActionsI thought the comment explains this. nnode itself will be made visible later, with modifications to both tn_link_target and tn_link_smr already stored anyone who deferences it sees both set one way or the other. acq barrier would be needed if nnode itself was already visible. mjg: I thought the comment explains this. nnode itself will be made visible later, with… | |||||
Done Inline Actionsnot mentioning data dependency barriers, if only as not needed on all supported architectures, sounds like a bug in the manpage. mjg: not mentioning data dependency barriers, if only as not needed on all supported architectures… | |||||
Not Done Inline ActionsFreeBSD as well as C11 memory models do not operate with barriers, they provide fences. Data barrier is subsumed by appropriate fence use. That said, again, what is the acquire residual to the store_rel(tn_link_smr) ? rel does not have any effect unless paired with acq. kib: FreeBSD as well as C11 memory models do not operate with barriers, they provide fences. Data… | |||||
break; | break; | ||||
case VREG: | case VREG: | ||||
obj = nnode->tn_reg.tn_aobj = | obj = nnode->tn_reg.tn_aobj = | ||||
vm_pager_allocate(OBJT_SWAP, NULL, 0, VM_PROT_DEFAULT, 0, | vm_pager_allocate(OBJT_SWAP, NULL, 0, VM_PROT_DEFAULT, 0, | ||||
NULL /* XXXKIB - tmpfs needs swap reservation */); | NULL /* XXXKIB - tmpfs needs swap reservation */); | ||||
VM_OBJECT_WLOCK(obj); | VM_OBJECT_WLOCK(obj); | ||||
/* OBJ_TMPFS is set together with the setting of vp->v_object */ | /* OBJ_TMPFS is set together with the setting of vp->v_object */ | ||||
Show All 36 Lines | tmpfs_free_node(struct tmpfs_mount *tmp, struct tmpfs_node *node) | ||||
} | } | ||||
} | } | ||||
bool | bool | ||||
tmpfs_free_node_locked(struct tmpfs_mount *tmp, struct tmpfs_node *node, | tmpfs_free_node_locked(struct tmpfs_mount *tmp, struct tmpfs_node *node, | ||||
bool detach) | bool detach) | ||||
{ | { | ||||
vm_object_t uobj; | vm_object_t uobj; | ||||
char *symlink; | |||||
bool last; | bool last; | ||||
TMPFS_MP_ASSERT_LOCKED(tmp); | TMPFS_MP_ASSERT_LOCKED(tmp); | ||||
TMPFS_NODE_ASSERT_LOCKED(node); | TMPFS_NODE_ASSERT_LOCKED(node); | ||||
last = refcount_release(&node->tn_refcount); | last = refcount_release(&node->tn_refcount); | ||||
if (node->tn_attached && (detach || last)) { | if (node->tn_attached && (detach || last)) { | ||||
MPASS(tmp->tm_nodes_inuse > 0); | MPASS(tmp->tm_nodes_inuse > 0); | ||||
Show All 19 Lines | #endif | ||||
case VDIR: | case VDIR: | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
case VFIFO: | case VFIFO: | ||||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||||
case VSOCK: | case VSOCK: | ||||
break; | break; | ||||
case VLNK: | case VLNK: | ||||
free(node->tn_link, M_TMPFSNAME); | symlink = node->tn_link_target; | ||||
atomic_store_ptr((uintptr_t *)&node->tn_link_target, | |||||
Not Done Inline ActionsWhy this store is release ? kib: Why this store is release ? | |||||
Done Inline Actionscopy pasto mjg: copy pasto | |||||
(uintptr_t)NULL); | |||||
if (atomic_load_char(&node->tn_link_smr)) | |||||
cache_symlink_free(symlink); | |||||
else | |||||
free(symlink, M_TMPFSNAME); | |||||
break; | break; | ||||
case VREG: | case VREG: | ||||
uobj = node->tn_reg.tn_aobj; | uobj = node->tn_reg.tn_aobj; | ||||
if (uobj != NULL) { | if (uobj != NULL) { | ||||
if (uobj->size != 0) | if (uobj->size != 0) | ||||
atomic_subtract_long(&tmp->tm_pages_used, uobj->size); | atomic_subtract_long(&tmp->tm_pages_used, uobj->size); | ||||
KASSERT((uobj->flags & OBJ_TMPFS) == 0, | KASSERT((uobj->flags & OBJ_TMPFS) == 0, | ||||
▲ Show 20 Lines • Show All 1,521 Lines • Show Last 20 Lines |
This store is release to ensure that symlink_smr update is visible if tn_link_target is non-NULL, right ?