Skip to content
Open
131 changes: 112 additions & 19 deletions drivers/media/usb/uvc/uvc_ctrl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1282,6 +1282,40 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
}

static void uvc_ctrl_set_handle(struct uvc_fh *handle, struct uvc_control *ctrl,
struct uvc_fh *new_handle)
{
lockdep_assert_held(&handle->chain->ctrl_mutex);

if (new_handle) {
if (ctrl->handle)
dev_warn_ratelimited(&handle->stream->dev->udev->dev,
"UVC non compliance: Setting an async control with a pending operation.");

if (new_handle == ctrl->handle)
return;

if (ctrl->handle) {
WARN_ON(!ctrl->handle->pending_async_ctrls);
if (ctrl->handle->pending_async_ctrls)
ctrl->handle->pending_async_ctrls--;
}

ctrl->handle = new_handle;
handle->pending_async_ctrls++;
return;
}

/* Cannot clear the handle for a control not owned by us.*/
if (WARN_ON(ctrl->handle != handle))
return;

ctrl->handle = NULL;
if (WARN_ON(!handle->pending_async_ctrls))
return;
handle->pending_async_ctrls--;
}

void uvc_ctrl_status_event(struct uvc_video_chain *chain,
struct uvc_control *ctrl, const u8 *data)
{
Expand All @@ -1292,7 +1326,8 @@ void uvc_ctrl_status_event(struct uvc_video_chain *chain,
mutex_lock(&chain->ctrl_mutex);

handle = ctrl->handle;
ctrl->handle = NULL;
if (handle)
uvc_ctrl_set_handle(handle, ctrl, NULL);

list_for_each_entry(mapping, &ctrl->info.mappings, list) {
s32 value = __uvc_ctrl_get_value(mapping, data);
Expand Down Expand Up @@ -1500,7 +1535,10 @@ int uvc_ctrl_begin(struct uvc_video_chain *chain)
}

static int uvc_ctrl_commit_entity(struct uvc_device *dev,
struct uvc_entity *entity, int rollback)
struct uvc_fh *handle,
struct uvc_entity *entity,
int rollback,
struct uvc_control **err_ctrl)
{
struct uvc_control *ctrl;
unsigned int i;
Expand Down Expand Up @@ -1542,30 +1580,64 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,

ctrl->dirty = 0;

if (ret < 0)
if (ret < 0) {
if (err_ctrl)
*err_ctrl = ctrl;
return ret;
}

if (!rollback && handle &&
ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
uvc_ctrl_set_handle(handle, ctrl, handle);
}

return 0;
}

static int uvc_ctrl_find_ctrl_idx(struct uvc_entity *entity,
struct v4l2_ext_controls *ctrls,
struct uvc_control *uvc_control)
{
struct uvc_control_mapping *mapping = NULL;
struct uvc_control *ctrl_found = NULL;
unsigned int i;

if (!entity)
return ctrls->count;

for (i = 0; i < ctrls->count; i++) {
__uvc_find_control(entity, ctrls->controls[i].id, &mapping,
&ctrl_found, 0);
if (uvc_control == ctrl_found)
return i;
}

return ctrls->count;
}

int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
const struct v4l2_ext_control *xctrls,
unsigned int xctrls_count)
struct v4l2_ext_controls *ctrls)
{
struct uvc_video_chain *chain = handle->chain;
struct uvc_control *err_ctrl;
struct uvc_entity *entity;
int ret = 0;

/* Find the control. */
list_for_each_entry(entity, &chain->entities, chain) {
ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
if (ret < 0)
ret = uvc_ctrl_commit_entity(chain->dev, handle, entity,
rollback, &err_ctrl);
if (ret < 0) {
if (ctrls)
ctrls->error_idx =
uvc_ctrl_find_ctrl_idx(entity, ctrls,
err_ctrl);
goto done;
}
}

if (!rollback)
uvc_ctrl_send_events(handle, xctrls, xctrls_count);
uvc_ctrl_send_events(handle, ctrls->controls, ctrls->count);
done:
mutex_unlock(&chain->ctrl_mutex);
return ret;
Expand Down Expand Up @@ -1694,9 +1766,6 @@ int uvc_ctrl_set(struct uvc_fh *handle,
mapping->set(mapping, value,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));

if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
ctrl->handle = handle;

ctrl->dirty = 1;
ctrl->modified = 1;
return 0;
Expand Down Expand Up @@ -1862,7 +1931,7 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control_query *xqry)
{
struct uvc_entity *entity;
struct uvc_entity *entity, *iter;
struct uvc_control *ctrl;
unsigned int i;
bool found;
Expand All @@ -1872,16 +1941,16 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
int ret;

/* Find the extension unit. */
found = false;
list_for_each_entry(entity, &chain->entities, chain) {
if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT &&
entity->id == xqry->unit) {
found = true;
entity = NULL;
list_for_each_entry(iter, &chain->entities, chain) {
if (UVC_ENTITY_TYPE(iter) == UVC_VC_EXTENSION_UNIT &&
iter->id == xqry->unit) {
entity = iter;
break;
}
}

if (!found) {
if (!entity) {
uvc_dbg(chain->dev, CONTROL, "Extension unit %u not found\n",
xqry->unit);
return -ENOENT;
Expand Down Expand Up @@ -2018,7 +2087,7 @@ int uvc_ctrl_restore_values(struct uvc_device *dev)
ctrl->dirty = 1;
}

ret = uvc_ctrl_commit_entity(dev, entity, 0);
ret = uvc_ctrl_commit_entity(dev, NULL, entity, 0, NULL);
if (ret < 0)
return ret;
}
Expand Down Expand Up @@ -2346,6 +2415,30 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
return 0;
}

void uvc_ctrl_cleanup_fh(struct uvc_fh *handle)
{
struct uvc_entity *entity;

mutex_lock(&handle->chain->ctrl_mutex);

if (!handle->pending_async_ctrls) {
mutex_unlock(&handle->chain->ctrl_mutex);
return;
}

list_for_each_entry(entity, &handle->chain->dev->entities, list) {
unsigned int i;
for (i = 0; i < entity->ncontrols; ++i) {
if (entity->controls[i].handle != handle)
continue;
uvc_ctrl_set_handle(handle, &entity->controls[i], NULL);
}
}

WARN_ON(handle->pending_async_ctrls);
mutex_unlock(&handle->chain->ctrl_mutex);
}

/*
* Cleanup device controls.
*/
Expand Down
9 changes: 7 additions & 2 deletions drivers/media/usb/uvc/uvc_v4l2.c
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,8 @@ static int uvc_v4l2_release(struct file *file)

uvc_dbg(stream->dev, CALLS, "%s\n", __func__);

uvc_ctrl_cleanup_fh(handle);

/* Only free resources if this is a privileged handle. */
if (uvc_has_privileges(handle))
uvc_queue_release(&stream->queue);
Expand Down Expand Up @@ -1024,6 +1026,7 @@ static int uvc_ioctl_s_ctrl(struct file *file, void *fh,
struct uvc_fh *handle = fh;
struct uvc_video_chain *chain = handle->chain;
struct v4l2_ext_control xctrl;
struct v4l2_ext_controls xctrls;
int ret;

memset(&xctrl, 0, sizeof(xctrl));
Expand All @@ -1040,7 +1043,9 @@ static int uvc_ioctl_s_ctrl(struct file *file, void *fh,
return ret;
}

ret = uvc_ctrl_commit(handle, &xctrl, 1);
xctrls.controls = &xctrl;
xctrls.count = 1;
ret = uvc_ctrl_commit(handle, &xctrls);
if (ret < 0)
return ret;

Expand Down Expand Up @@ -1120,7 +1125,7 @@ static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle,
ctrls->error_idx = 0;

if (commit)
return uvc_ctrl_commit(handle, ctrls->controls, ctrls->count);
return uvc_ctrl_commit(handle, ctrls);
else
return uvc_ctrl_rollback(handle);
}
Expand Down
19 changes: 12 additions & 7 deletions drivers/media/usb/uvc/uvcvideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,11 @@ struct uvc_video_chain {
struct uvc_entity *processing; /* Processing unit */
struct uvc_entity *selector; /* Selector unit */

struct mutex ctrl_mutex; /* Protects ctrl.info */
struct mutex ctrl_mutex; /*
* Protects ctrl.info,
* ctrl.handle and
* uvc_fh.pending_async_ctrls
*/

struct v4l2_prio_state prio; /* V4L2 priority state */
u32 caps; /* V4L2 chain-wide caps */
Expand Down Expand Up @@ -721,6 +725,7 @@ struct uvc_fh {
struct uvc_video_chain *chain;
struct uvc_streaming *stream;
enum uvc_handle_state state;
unsigned int pending_async_ctrls;
};

struct uvc_driver {
Expand Down Expand Up @@ -883,17 +888,15 @@ void uvc_ctrl_status_event(struct uvc_video_chain *chain,

int uvc_ctrl_begin(struct uvc_video_chain *chain);
int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
const struct v4l2_ext_control *xctrls,
unsigned int xctrls_count);
struct v4l2_ext_controls *ctrls);
static inline int uvc_ctrl_commit(struct uvc_fh *handle,
const struct v4l2_ext_control *xctrls,
unsigned int xctrls_count)
struct v4l2_ext_controls *ctrls)
{
return __uvc_ctrl_commit(handle, 0, xctrls, xctrls_count);
return __uvc_ctrl_commit(handle, 0, ctrls);
}
static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
{
return __uvc_ctrl_commit(handle, 1, NULL, 0);
return __uvc_ctrl_commit(handle, 1, NULL);
}

int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
Expand All @@ -902,6 +905,8 @@ int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control_query *xqry);

void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);

/* Utility functions */
void uvc_simplify_fraction(u32 *numerator, u32 *denominator,
unsigned int n_terms, unsigned int threshold);
Expand Down
6 changes: 5 additions & 1 deletion fs/nfsd/nfsctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1524,7 +1524,10 @@ static int __init init_nfsd(void)
retval = nfsd4_init_pnfs();
if (retval)
goto out_free_slabs;
nfsd_stat_init(); /* Statistics */
if (!nfsd_stat_init()) { /* Statistics */
retval = -ENOMEM;
goto out_free_pnfs;
}
retval = nfsd_drc_slab_create();
if (retval)
goto out_free_stat;
Expand All @@ -1549,6 +1552,7 @@ static int __init init_nfsd(void)
nfsd_drc_slab_free();
out_free_stat:
nfsd_stat_shutdown();
out_free_pnfs:
nfsd4_exit_pnfs();
out_free_slabs:
nfsd4_free_slabs();
Expand Down
4 changes: 2 additions & 2 deletions fs/nfsd/stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ static const struct file_operations nfsd_proc_fops = {
.release = single_release,
};

void
struct proc_dir_entry *
nfsd_stat_init(void)
{
svc_proc_register(&init_net, &nfsd_svcstats, &nfsd_proc_fops);
return svc_proc_register(&init_net, &nfsd_svcstats, &nfsd_proc_fops);
}

void
Expand Down
2 changes: 1 addition & 1 deletion fs/nfsd/stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ struct nfsd_stats {
extern struct nfsd_stats nfsdstats;
extern struct svc_stat nfsd_svcstats;

void nfsd_stat_init(void);
struct proc_dir_entry * nfsd_stat_init(void);
void nfsd_stat_shutdown(void);

#endif /* _NFSD_STATS_H */
39 changes: 31 additions & 8 deletions net/vmw_vsock/af_vsock.c
Original file line number Diff line number Diff line change
Expand Up @@ -1408,17 +1408,40 @@ static int vsock_stream_connect(struct socket *sock, struct sockaddr *addr,
timeout = schedule_timeout(timeout);
lock_sock(sk);

if (signal_pending(current)) {
err = sock_intr_errno(timeout);
sk->sk_state = sk->sk_state == TCP_ESTABLISHED ? TCP_CLOSING : TCP_CLOSE;
sock->state = SS_UNCONNECTED;
vsock_transport_cancel_pkt(vsk);
goto out_wait;
} else if (timeout == 0) {
err = -ETIMEDOUT;
/* Connection established. Whatever happens to socket once we
* release it, that's not connect()'s concern. No need to go
* into signal and timeout handling. Call it a day.
*
* Note that allowing to "reset" an already established socket
* here is racy and insecure.
*/
if (sk->sk_state == TCP_ESTABLISHED)
break;

/* If connection was _not_ established and a signal/timeout came
* to be, we want the socket's state reset. User space may want
* to retry.
*
* sk_state != TCP_ESTABLISHED implies that socket is not on
* vsock_connected_table. We keep the binding and the transport
* assigned.
*/
if (signal_pending(current) || timeout == 0) {
err = timeout == 0 ? -ETIMEDOUT : sock_intr_errno(timeout);

/* Listener might have already responded with
* VIRTIO_VSOCK_OP_RESPONSE. Its handling expects our
* sk_state == TCP_SYN_SENT, which hereby we break.
* In such case VIRTIO_VSOCK_OP_RST will follow.
*/
sk->sk_state = TCP_CLOSE;
sock->state = SS_UNCONNECTED;

/* Try to cancel VIRTIO_VSOCK_OP_REQUEST skb sent out by
* transport->connect().
*/
vsock_transport_cancel_pkt(vsk);

goto out_wait;
}

Expand Down
Loading