Index: sys/dev/iscsi/icl_soft.c =================================================================== --- sys/dev/iscsi/icl_soft.c +++ sys/dev/iscsi/icl_soft.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include +#include #include #include #include @@ -56,6 +57,7 @@ #include #include #include +#include #include #include @@ -129,7 +131,9 @@ static icl_conn_pdu_free_t icl_soft_conn_pdu_free; static icl_conn_pdu_data_segment_length_t icl_soft_conn_pdu_data_segment_length; +static icl_conn_pdu_append_bio_t icl_soft_conn_pdu_append_bio; static icl_conn_pdu_append_data_t icl_soft_conn_pdu_append_data; +static icl_conn_pdu_get_bio_t icl_soft_conn_pdu_get_bio; static icl_conn_pdu_get_data_t icl_soft_conn_pdu_get_data; static icl_conn_pdu_queue_t icl_soft_conn_pdu_queue; static icl_conn_pdu_queue_cb_t icl_soft_conn_pdu_queue_cb; @@ -149,7 +153,9 @@ KOBJMETHOD(icl_conn_pdu_free, icl_soft_conn_pdu_free), KOBJMETHOD(icl_conn_pdu_data_segment_length, icl_soft_conn_pdu_data_segment_length), + KOBJMETHOD(icl_conn_pdu_append_bio, icl_soft_conn_pdu_append_bio), KOBJMETHOD(icl_conn_pdu_append_data, icl_soft_conn_pdu_append_data), + KOBJMETHOD(icl_conn_pdu_get_bio, icl_soft_conn_pdu_get_bio), KOBJMETHOD(icl_conn_pdu_get_data, icl_soft_conn_pdu_get_data), KOBJMETHOD(icl_conn_pdu_queue, icl_soft_conn_pdu_queue), KOBJMETHOD(icl_conn_pdu_queue_cb, icl_soft_conn_pdu_queue_cb), @@ -1066,6 +1072,142 @@ return (SU_OK); } +static void +icl_soft_free_mext_pg(struct mbuf *m) +{ + struct icl_soft_pdu *isp; + + M_ASSERTEXTPG(m); + + /* + * Nothing to do for the pages; they are owned by the PDU / + * I/O request. + */ + + /* Drop reference on the PDU. */ + isp = m->m_ext.ext_arg1; + if (atomic_fetchadd_int(&isp->ref_cnt, -1) == 1) + icl_soft_pdu_call_cb(&isp->ip); +} + +static int +icl_soft_conn_pdu_append_bio(struct icl_conn *ic, struct icl_pdu *request, + struct bio *bp, size_t offset, size_t len, int flags) +{ + struct icl_soft_pdu *isp = (struct icl_soft_pdu *)request; + struct mbuf *m, *m_tail; + vm_offset_t vaddr; + size_t mtodo, page_offset, todo; + boolean_t mapped; + int i; + + KASSERT(len > 0, ("len == 0")); + + m_tail = request->ip_data_mbuf; + if (m_tail != NULL) + for (; m_tail->m_next != NULL; m_tail = m_tail->m_next) + ; + + MPASS(bp->bio_flags & BIO_UNMAPPED); + if (offset < PAGE_SIZE - bp->bio_ma_offset) { + page_offset = bp->bio_ma_offset + offset; + i = 0; + } else { + offset -= PAGE_SIZE - bp->bio_ma_offset; + for (i = 1; offset >= PAGE_SIZE; i++) + offset -= PAGE_SIZE; + page_offset = offset; + } + + if (flags & ICL_NOCOPY) { + m = NULL; + while (len > 0) { + if (m == NULL) { + m = mb_alloc_ext_pgs(flags & ~ICL_NOCOPY, + icl_soft_free_mext_pg); + if (__predict_false(m == NULL)) + return (ENOMEM); + atomic_add_int(&isp->ref_cnt, 1); + m->m_ext.ext_arg1 = isp; + m->m_epg_1st_off = page_offset; + } + + todo = MIN(len, PAGE_SIZE - page_offset); + + m->m_epg_pa[m->m_epg_npgs] = + VM_PAGE_TO_PHYS(bp->bio_ma[i]); + m->m_epg_npgs++; + m->m_epg_last_len = todo; + m->m_len += todo; + m->m_ext.ext_size += PAGE_SIZE; + MBUF_EXT_PGS_ASSERT_SANITY(m); + + if (m->m_epg_npgs == MBUF_PEXT_MAX_PGS) { + if (m_tail != NULL) + m_tail->m_next = m; + else + request->ip_data_mbuf = m; + m_tail = m; + request->ip_data_len += m->m_len; + m = NULL; + } + + page_offset = 0; + len -= todo; + i++; + } + + if (m != NULL) { + if (m_tail != NULL) + m_tail->m_next = m; + else + request->ip_data_mbuf = m; + request->ip_data_len += m->m_len; + } + return (0); + } + + m = m_getm2(NULL, len, flags, MT_DATA, 0); + if (__predict_false(m == NULL)) + return (ENOMEM); + + if (request->ip_data_mbuf == NULL) { + request->ip_data_mbuf = m; + request->ip_data_len = len; + } else { + m_tail->m_next = m; + request->ip_data_len += len; + } + + while (len > 0) { + todo = MIN(len, PAGE_SIZE - page_offset); + + mapped = pmap_map_io_transient(bp->bio_ma + i, &vaddr, 1, + FALSE); + + do { + mtodo = min(todo, M_SIZE(m) - m->m_len); + memcpy(mtod(m, char *) + m->m_len, (char *)vaddr + + page_offset, mtodo); + m->m_len += mtodo; + if (m->m_len == M_SIZE(m)) + m = m->m_next; + page_offset += mtodo; + todo -= mtodo; + } while (todo > 0); + + if (__predict_false(mapped)) + pmap_unmap_io_transient(bp->bio_ma + 1, &vaddr, 1, + FALSE); + + page_offset = 0; + len -= todo; + i++; + } + + return (0); +} + static int icl_soft_conn_pdu_append_data(struct icl_conn *ic, struct icl_pdu *request, const void *addr, size_t len, int flags) @@ -1114,6 +1256,44 @@ return (0); } +void +icl_soft_conn_pdu_get_bio(struct icl_conn *ic, struct icl_pdu *ip, + size_t pdu_off, struct bio *bp, size_t bio_off, size_t len) +{ + vm_offset_t vaddr; + size_t page_offset, todo; + boolean_t mapped; + int i; + + MPASS(bp->bio_flags & BIO_UNMAPPED); + if (bio_off < PAGE_SIZE - bp->bio_ma_offset) { + page_offset = bp->bio_ma_offset + bio_off; + i = 0; + } else { + bio_off -= PAGE_SIZE - bp->bio_ma_offset; + for (i = 1; bio_off >= PAGE_SIZE; i++) + bio_off -= PAGE_SIZE; + page_offset = bio_off; + } + + while (len > 0) { + todo = MIN(len, PAGE_SIZE - page_offset); + + mapped = pmap_map_io_transient(bp->bio_ma + i, &vaddr, 1, + FALSE); + m_copydata(ip->ip_data_mbuf, pdu_off, todo, (char *)vaddr + + page_offset); + if (__predict_false(mapped)) + pmap_unmap_io_transient(bp->bio_ma + 1, &vaddr, 1, + FALSE); + + page_offset = 0; + pdu_off += todo; + len -= todo; + i++; + } +} + void icl_soft_conn_pdu_get_data(struct icl_conn *ic, struct icl_pdu *ip, size_t off, void *addr, size_t len) @@ -1182,7 +1362,7 @@ #endif ic->ic_name = name; ic->ic_offload = "None"; - ic->ic_unmapped = false; + ic->ic_unmapped = true; return (ic); }