device가 user buffer를 쓰는 동안 process가 munmap()하면 어떻게 될까요. 올바른 pin을 보유했다면 physical folio가 즉시 재사용되지는 않습니다. 그러나 원래 virtual mapping이 계속 존재한다는 보장은 없습니다. pin이 지키는 대상부터 정확히 구분해야 합니다.
GET과 PIN의 목적
get_user_pages*()의 FOLL_GET은 일반 reference를 얻으며 대응하는 put이 필요합니다. pin_user_pages*()는 FOLL_PIN protocol을 사용하고 unpin_user_page*()로 반환합니다. 같은 호출에서 둘을 함께 지정하지 않습니다. 서로 다른 caller가 같은 folio에 서로 다른 reference를 가지는 것은 가능합니다.
선택 기준은 보유 시간 하나가 아닙니다. data를 외부에서 접근하는 DMA·direct I/O 같은 용도에는 짧은 작업이어도 pin 계약이 필요할 수 있습니다. metadata만 다루거나 적절한 lock 아래 제한된 작업을 하는 reference와 구별해야 합니다. device 사용 전에 pin을 얻었다고 DMA mapping, IOMMU와 cache-coherency protocol까지 완료된 것도 아닙니다.
device나 kernel이 page 내용을 쓸 예정이면 획득 단계부터 FOLL_WRITE를 요청해야 합니다. WRITE 없이 얻은 page를 kernel alias로 쓰는 것은 read-only 획득 계약을 우회합니다. 실제 write를 마친 뒤에는 해당 API에 맞는 dirty 처리를 하고 put 또는 unpin해야 하며, 이 순서에서도 data 사용이 끝나기 전에 reference를 먼저 내려놓을 수는 없습니다. v6.0 remote GUP의 주석은 WRITE 의도와 write 후 dirty 처리를 별도의 책임으로 명시합니다.

pin accounting은 모든 folio에서 같지 않습니다
order-0에서는 GUP_PIN_COUNTING_BIAS를 reference에 더하는 방식으로 pin 가능성을 표시합니다. 그래서 folio_maybe_dma_pinned()는 정확한 모든 pin 개수가 아니라 보수적 판정일 수 있습니다. 큰 folio에는 별도 pincount가 있습니다. Zero-page accounting도 tag를 구별해야 합니다. 아래 v6.0 helper에는 뒤 version의 zero-page 생략 동작을 덧씌우지 않았습니다. 모든 반환 page에서 refcount가 같은 상수만큼 변한다고 계산해서는 안 됩니다.
pin acquired/released 통계는 다른 diagnostic 단서이지만 process의 전체 DMA 수명을 그대로 기록하는 counter는 아닙니다. refcount overflow와 large-folio split 경쟁도 pin helper의 검사 대상입니다. 획득한 API와 반환 API가 대칭이어야 bias와 pincount가 올바르게 줄어듭니다.
read pin도 CoW를 끊어야 할 수 있습니다
fork로 공유하는 anonymous page를 read-only로 pin한 뒤 process가 write해 다른 folio로 CoW하면 pin 보유자가 보는 대상과 process의 buffer가 갈라질 수 있습니다. GUP는 필요한 경우 UNSHARE fault를 통해 anonymous page의 배타성을 확보합니다. read pin이므로 PTE를 임의로 writable하게 만드는 것은 아닙니다.
pin된 exclusive anonymous page를 fork에서 그대로 공유 가능한 상태로 바꾸는 것도 제약을 받습니다. 여기서 exclusive는 refcount 하나와 동의어가 아닙니다. mlock()은 이러한 physical pin의 대안이 아니며 residency 정책입니다. 반대로 pin은 VMA의 생존을 보장하지 않습니다.
LONGTERM이 더 검사하는 것
FOLL_LONGTERM은 무기한에 가까운 data 접근 의도를 알리며 pin wrapper와 함께 사용합니다. movable zone·CMA 등 장기적으로 붙잡으면 안 되는 위치의 folio는 migration이 필요할 수 있습니다. DAX와 device memory에는 추가 제약이 있습니다. 이것을 단순히 “모든 page의 migratetype을 UNMOVABLE로 바꾼다”는 동작으로 설명해서는 안 됩니다.
이런 제약 때문에 긴 pin 요청에는 pin을 풀고 migration한 뒤 다시 주소를 조회하는 slow path가 필요할 수 있습니다. pin을 얻었다는 한 순간의 성공과 장기 사용에 적합하다는 판단은 다른 단계입니다.
보수적 판정의 true와 false는 대칭이 아닙니다
아래 전체 helper는 v6.0 정의입니다. Small folio의 일반 reference가 충분히 많아도 true가 나올 수 있습니다. 그래서 true는 “DMA 사용을 입증했다”가 아니라 해당 pin 가능성을 배제하지 못했다는 판정입니다. False를 이용해 pin 부재를 판단하는 caller와 true의 false positive를 허용하는 caller 설계가 함께 필요합니다. Bias는 2^10 = 1024이고 refcount unsigned 비교는 overflow 영역도 보수적으로 취급하려는 의도입니다.
/* SPDX-License-Identifier: GPL-2.0 */
/* Upstream Linux v6.0: include/linux/mm.h:1498-1513
* Exact source lines; kernel context, not a standalone program or book restoration.
*/
static inline bool folio_maybe_dma_pinned(struct folio *folio)
{
if (folio_test_large(folio))
return atomic_read(folio_pincount_ptr(folio)) > 0;
/*
* folio_ref_count() is signed. If that refcount overflows, then
* folio_ref_count() returns a negative value, and callers will avoid
* further incrementing the refcount.
*
* Here, for that overflow case, use the sign bit to count a little
* bit higher via unsigned math, and thus still get an accurate result.
*/
return ((unsigned int)folio_ref_count(folio)) >=
GUP_PIN_COUNTING_BIAS;
}
MMF_HAS_PINNED는 마지막 unpin 때 반드시 0으로 돌아오는 현재 pin 개수 flag가 아니라 이 mm에서 pin 경로가 사용된 이력을 보수적으로 남기는 flag입니다. Reclaim, soft-dirty, anonymous exclusive 공유, fork의 CoW 정책은 같은 pin 정보를 서로 다른 이유로 참고합니다. 이들 중 하나를 보았다고 pin이 contents 동기화나 DMA 완료를 보장한다고 결론 내리면 안 됩니다.
Source: Linux v6.0 mm.h, mm/gup.c. Historical reference이고 book exact tag는 미확정입니다. 원래 notice와 COPYING, GPL-2.0를 보존했습니다. Compile·runtime 검증은 하지 않았으며 v6.18은 별도 고정 비교 tag입니다.
참고 자료
Lorenzo Stoakes, The Linux Memory Manager, Chapter 8 §8.1.2–§8.1.4. 용도별 계약은 v6.18 pin_user_pages 문서 원본, accounting과 unshare는 mm/gup.c를 참고하실 수 있습니다.