On 04.06.24 11:59, Andreas Hindborg wrote: > Benno Lossin <benno.lossin@xxxxxxxxx> writes: > > [...] > >>>>> +impl<T: Operations> OperationsVTable<T> { >>>>> + /// This function is called by the C kernel. A pointer to this function is >>>>> + /// installed in the `blk_mq_ops` vtable for the driver. >>>>> + /// >>>>> + /// # Safety >>>>> + /// >>>>> + /// - The caller of this function must ensure `bd` is valid >>>>> + /// and initialized. The pointees must outlive this function. >>>> >>>> Until when do the pointees have to be alive? "must outlive this >>>> function" could also be the case if the pointees die immediately after >>>> this function returns. >>> >>> It should not be plural. What I intended to communicate is that what >>> `bd` points to must be valid for read for the duration of the function >>> call. I think that is what "The pointee must outlive this function" >>> states? Although when we talk about lifetime of an object pointed to by >>> a pointer, I am not sure about the correct way to word this. Do we talk >>> about the lifetime of the pointer or the lifetime of the pointed to >>> object (the pointee). We should not use the same wording for the pointer >>> and the pointee. >>> >>> How about: >>> >>> /// - The caller of this function must ensure that the pointee of `bd` is >>> /// valid for read for the duration of this function. >> >> But this is not enough for it to be sound, right? You create an `ARef` >> from `bd.rq`, which potentially lives forever. You somehow need to >> require that the pointer `bd` stays valid for reads and (synchronized) >> writes until the request is ended (probably via `blk_mq_end_request`). > > The statement does not say anything about `*((*bd).rq)`. `*bd` needs to > be valid only for the duration of the function. It carries a pointer to > a `struct request` in the `rq` field. The pointee of that pointer must > be exclusively owned by the driver until the request is done. > > Maybe like this: > > # Safety > > - The caller of this function must ensure that the pointee of `bd` is > valid for read for the duration of this function. "valid for reads" > - This function must be called for an initialized and live `hctx`. That > is, `Self::init_hctx_callback` was called and > `Self::exit_hctx_callback()` was not yet called. > - `(*bd).rq` must point to an initialized and live `bindings:request`. > That is, `Self::init_request_callback` was called but > `Self::exit_request_callback` was not yet called for the request. > - `(*bd).rq` must be owned by the driver. That is, the block layer must > promise to not access the request until the driver calls > `bindings::blk_mq_end_request` for the request. Sounds good! > [...] > >>>>> + /// This function is called by the C kernel. A pointer to this function is >>>>> + /// installed in the `blk_mq_ops` vtable for the driver. >>>>> + /// >>>>> + /// # Safety >>>>> + /// >>>>> + /// This function may only be called by blk-mq C infrastructure. `set` must >> >> `set` doesn't exist (`_set` does), you are also not using this >> requirement. > > Would be nice if there was a way in `rustdoc` no name arguments > explicitly. > >> >>>>> + /// point to an initialized `TagSet<T>`. >>>>> + unsafe extern "C" fn init_request_callback( >>>>> + _set: *mut bindings::blk_mq_tag_set, >>>>> + rq: *mut bindings::request, >>>>> + _hctx_idx: core::ffi::c_uint, >>>>> + _numa_node: core::ffi::c_uint, >>>>> + ) -> core::ffi::c_int { >>>>> + from_result(|| { >>>>> + // SAFETY: The `blk_mq_tag_set` invariants guarantee that all >>>>> + // requests are allocated with extra memory for the request data. >>>> >>>> What guarantees that the right amount of memory has been allocated? >>>> AFAIU that is guaranteed by the `TagSet` (but there is no invariant). >>> >>> It is by C API contract. `TagSet`::try_new` (now `new`) writes >>> `cmd_size` into the `struct blk_mq_tag_set`. That is picked up by >>> `blk_mq_alloc_tag_set` to allocate the right amount of space for each request. >>> >>> The invariant here is on the C type. Perhaps the wording is wrong. I am >>> not exactly sure how to express this. How about this: >>> >>> // SAFETY: We instructed `blk_mq_alloc_tag_set` to allocate requests >>> // with extra memory for the request data when we called it in >>> // `TagSet::new`. >> >> I think you need a safety requirement on the function: `rq` points to a >> valid `Request`. Then you could just use `Request::wrapper_ptr` instead >> of the line below. > > I cannot require `rq` to point to a valid `Request`, because that would > require the private data area to already be initialized as a valid > `RequestDataWrapper`. Using the `wrapper_ptr` is good 👍. How is this: > > > /// # Safety > /// > /// - This function may only be called by blk-mq C infrastructure. > /// - `_set` must point to an initialized `TagSet<T>`. > /// - `rq` must point to an initialized `bindings::request`. > /// - The allocation pointed to by `rq` must be at the size of `Request` > /// plus the size of `RequestDataWrapper`. Also sounds good to me. --- Cheers, Benno