On 10/19/18 2:24 AM, Douglas Gilbert wrote: > + if (sfp->tot_fd_thresh) > + sfp->sum_fd_dlens += align_sz; > What lock protects sfp->sum_fd_dlens? > /* > @@ -2216,38 +2401,85 @@ sg_add_request(struct sg_fd *sfp) > * data length exceeds rem_sgat_thresh then the data (or sgat) is > * cleared and the request is appended to the tail of the free list. > */ > -static int > +static void > sg_remove_request(struct sg_fd *sfp, struct sg_request *srp) > { > + bool reserve; > unsigned long iflags; > - int res = 0; > + const char *cp = "head"; > + char b[64]; > > - if (!sfp || !srp || list_empty(&sfp->rq_list)) > - return res; > + if (WARN_ON(!sfp || !srp)) > + return; > write_lock_irqsave(&sfp->rq_list_lock, iflags); > - if (!list_empty(&srp->entry)) { > - list_del(&srp->entry); > - srp->parentfp = NULL; > - res = 1; > - } > + spin_lock(&srp->rq_entry_lck); > + /* > + * N.B. sg_request object not de-allocated (freed). The contents of > + * rq_list and rq_free_list lists are de-allocated (freed) when the > + * owning file descriptor is closed. The free list acts as a LIFO. > + * This can improve the chance of a cache hit when request is re-used. > + */ > + reserve = (sfp->reserve_srp == srp); > + if (reserve || srp->data.dlen <= sfp->rem_sgat_thresh) { > + list_del(&srp->rq_entry); > + if (srp->data.dlen > 0) > + list_add(&srp->free_entry, &sfp->rq_free_list); > + else { > + list_add_tail(&srp->free_entry, &sfp->rq_free_list); > + cp = "tail"; > + } > + snprintf(b, sizeof(b), "%ssrp=0x%p move to fl %s", > + (reserve ? "reserve " : ""), srp, cp); > + } else { > + srp->rq_state = SG_RQ_BUSY; > + list_del(&srp->rq_entry); > + spin_unlock(&srp->rq_entry_lck); > + write_unlock_irqrestore(&sfp->rq_list_lock, iflags); > + if (sfp->sum_fd_dlens) { > + u32 uv = srp->data.dlen; > + > + if (uv <= sfp->sum_fd_dlens) > + sfp->sum_fd_dlens -= uv; > + else { > + SG_LOG(2, sfp->parentdp, > + "%s: logic error this dlen > %s\n", > + __func__, "sum_fd_dlens"); > + sfp->sum_fd_dlens = 0; > + } > + } > + sg_remove_sgat(srp); > + /* don't kfree(srp), move clear request to tail of fl */ > + write_lock_irqsave(&sfp->rq_list_lock, iflags); > + spin_lock(&srp->rq_entry_lck); > + list_add_tail(&srp->free_entry, &sfp->rq_free_list); > + snprintf(b, sizeof(b), "clear sgat srp=0x%p move to fl tail", > + srp); > + } > > Here again, no lock protecting sfp->sum_fd_dlens. Incidentally, I have been using my own home-grown target-mode SCSI system for the past 16 years, but now I am starting to look into switching to SCST. I was just reading about their "SGV cache": http://scst.sourceforge.net/scst_pg.html It looks like it serves a similar purpose to what you are trying to accomplish with recycling the indirect I/O buffers between different requests. Perhaps you can borrow some inspiration from them (or even some code). Tony Battersby Cybernetics