On Sat, 5 Dec 2015, Steinar H. Gunderson wrote: > On Sat, Dec 05, 2015 at 12:36:51PM -0500, Alan Stern wrote: > > I meant that this "if" statement should test only uurb_start. If the > > test succeeds, a nested "if" statement should test buffer_length and > > return an error if it is too big. > > > > That's as opposed to the way you have it now, where if buffer_length is > > too big you return NULL. The system would then try to treat the > > coherent memory as a normal memory buffer, which may not be a good > > idea. > > OK. Why is it a problem to treat it as a normal memory buffer? (I understand > it would be slower, of course.) To tell the truth, I'm not sure whether it would be a problem or not. That's why I said it "may" not be a good idea. Mixing the usages would mean mapping the memory region for normal streaming DMA when it might already have been mapped for coherent DMA. Maybe that's okay; I don't know. It seems safest to avoid the issue. Besides, if the user program went to the trouble of mmap'ing a memory region and then ...: ... tried to present a USB buffer that overflowed the end of the region, we should point out the error. ... created a control or interrupt transfer with its USB buffer in the region, we should honor the program's request and perform a zerocopy I/O operation. > > You don't really need to do it earlier. An unnecessary calculation of > > num_sgs won't hurt. > > Is it unnecessary? I thought the test was really to force num_sgs==0 for the > DMA case, not to save a little arithmetic. Well, if you calculate num_sgs and then force it to 0 anyway, the calculation was unnecessary, right? > > Comments below. I'll skip much of the patch because it looks pretty > > good. > > Good, I guess we're finally converting on something acceptable to all parties :-) Definitely. BTW, in the future, could you put your patches in the body of the emails instead of making them attachments? That's how we normally do things on this mailing list; it makes replying and commenting on patches easier. > +static void dec_usb_memory_use_count(struct usb_memory *usbm, int *count) > +{ > + struct usb_dev_state *ps = usbm->ps; > + unsigned long flags; > + > + spin_lock_irqsave(&ps->lock, flags); > + --*count; > + if (usbm->urb_use_count == 0 && usbm->vma_use_count == 0) { > + list_del_init(&usbm->memlist); This can be list_del(), since you're about to deallocate usbm. > + spin_unlock_irqrestore(&ps->lock, flags); > + > + usb_free_coherent(ps->dev, usbm->size, usbm->mem, > + usbm->dma_handle); > + usbfs_decrease_memory_usage( > + usbm->size + sizeof(struct usb_memory)); > + kfree(usbm); > + } else { > + spin_unlock_irqrestore(&ps->lock, flags); > + } > +} ... > +static struct usb_memory * > +find_memory_area(struct usb_dev_state *ps, const struct usbdevfs_urb *uurb) > +{ > + struct usb_memory *usbm = NULL, *iter; > + unsigned long flags; > + unsigned long uurb_start = (unsigned long)uurb->buffer; > + > + spin_lock_irqsave(&ps->lock, flags); > + list_for_each_entry(iter, &ps->memory_list, memlist) { > + if (uurb_start >= iter->vm_start && > + uurb_start < iter->vm_start + iter->size) { > + if (uurb->buffer_length > iter->vm_start + iter->size - > + uurb_start) { > + usbm = ERR_PTR(-EINVAL); > + } else { > + usbm = iter; > + usbm->urb_use_count++; > + break; > + } The "break" belongs here, not where it is. > + } > + } > + spin_unlock_irqrestore(&ps->lock, flags); > + return usbm; > +} > + > static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb, > struct usbdevfs_iso_packet_desc __user *iso_frame_desc, > void __user *arg) > @@ -1372,9 +1525,17 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb > uurb->type = USBDEVFS_URB_TYPE_INTERRUPT; > goto interrupt_urb; > } > - num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE); > - if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize) > - num_sgs = 0; > + /* do not use SG buffers when memory mapped segments > + * are in use > + */ > + if (as->usbm) { > + num_sgs = DIV_ROUND_UP(uurb->buffer_length, > + USB_SG_SIZE); > + if (num_sgs == 1 || > + num_sgs > ps->dev->bus->sg_tablesize) { > + num_sgs = 0; > + } > + } No, no. Leave this part of the code unchanged. as hasn't even been allocated yet. > if (ep->streams) > stream_id = uurb->stream_id; > break; > @@ -1445,10 +1606,15 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb > if (ret) > goto error; > as->mem_usage = u; > + as->usbm = find_memory_area(ps, uurb); > + if (IS_ERR_VALUE(as->usbm)) { > + ret = PTR_ERR(as->usbm); > + goto error; > + } As pointed out repeatedly by the kbuild test robot, this should be IS_ERR, not IS_ERR_VALUE. Also, you need to set as->usbm back to NULL before jumping. At this point, set num_sgs to 0 if as->usbm is non-NULL. Actually, now that I look at the code in context, this whole section needs to go up a little bit, before before the calculation of u. If num_sgs is going to be forced to 0, we want to do it before the memory usage is computed. > if (num_sgs) { > as->urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist), > - GFP_KERNEL); > + GFP_KERNEL); Not relevant to the patch. > if (!as->urb->sg) { > ret = -ENOMEM; > goto error; > @@ -1476,21 +1642,26 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb > totlen -= u; > } > } else if (uurb->buffer_length > 0) { > - as->urb->transfer_buffer = kmalloc(uurb->buffer_length, > + if (as->usbm) { > + as->urb->transfer_buffer = as->usbm->mem; > + } else { > + as->urb->transfer_buffer = kmalloc(uurb->buffer_length, > GFP_KERNEL); > + } > if (!as->urb->transfer_buffer) { > ret = -ENOMEM; > goto error; > } This test may as well be part of the "else" clause. We know as->usbm->mem is always a valid address. > > - if (!is_in) { > + if (!is_in && as->usbm == NULL) { > if (copy_from_user(as->urb->transfer_buffer, > uurb->buffer, > uurb->buffer_length)) { > ret = -EFAULT; > goto error; > } > - } else if (uurb->type == USBDEVFS_URB_TYPE_ISO) { > + } else if (uurb->type == USBDEVFS_URB_TYPE_ISO && > + as->usbm == NULL) { My preferred style for avoiding these repeated tests goes like this: if (as->usbm) { ; /* Transfer buffer is okay as it is */ } else if (!is_in) { ... You don't have to copy my style, though. > /* > * Isochronous input data may end up being > * discontiguous if some of the packets are short. > @@ -1545,10 +1716,18 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb > isopkt = NULL; > as->ps = ps; > as->userurb = arg; > - if (is_in && uurb->buffer_length > 0) > + if (is_in && uurb->buffer_length > 0 && as->usbm == NULL) { > as->userbuffer = uurb->buffer; > - else > + } else { > as->userbuffer = NULL; > + } The braces aren't needed. In fact, the NULL assignment isn't needed either, since as was allocated by kzalloc. > + if (as->usbm != NULL) { > + unsigned long uurb_start = (unsigned long)uurb->buffer; > + > + as->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; > + as->urb->transfer_dma = as->usbm->dma_handle + > + (uurb_start - as->usbm->vm_start); You also should add (uurb_start - as->usbm->vm_start) to as->urb->transfer_buffer. Or if you want, you can make that adjustment where as->urb->transfer_buffer is set originally. Everything else looks okay. Alan Stern -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html