/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/*
* smb_oplock_wait / smb_oplock_broadcast
* When an oplock is being acquired, we must ensure that the acquisition
* response is submitted to the network stack before any other operation
* is permitted on the oplock.
* In smb_oplock_acquire, oplock.ol_xthread is set to point to the worker
* thread processing the command that is granting the oplock.
* Other threads accessing the oplock will be suspended in smb_oplock_wait().
* They will be awakened when the worker thread referenced in 'ol_xthread'
* calls smb_oplock_broadcast().
*
* The purpose of this mechanism is to prevent another thread from
* triggering an oplock break before the response conveying the grant
* has been sent.
*/
#include <smbsrv/smb_kproto.h>
(((level) == SMB_OPLOCK_EXCLUSIVE) || \
((level) == SMB_OPLOCK_BATCH))
static int smb_oplock_install_fem(smb_node_t *);
static void smb_oplock_uninstall_fem(smb_node_t *);
static void smb_oplock_wait(smb_node_t *);
static void smb_oplock_timedout(smb_node_t *);
void smb_oplock_clear_grant(smb_oplock_grant_t *);
static void smb_oplock_exec_async_break(void *);
static void smb_oplock_break_levelII_locked(smb_node_t *);
/*
* smb_oplock_install_fem
* Install fem monitor for cross protocol oplock breaking.
*/
static int
{
if (smb_fem_oplock_install(node) != 0) {
"failed to install fem monitor %s",
return (-1);
}
}
return (0);
}
/*
* smb_oplock_uninstall_fem
* Uninstall fem monitor for cross protocol oplock breaking.
*/
static void
{
}
}
/*
* This provides a way to fully disable oplocks, i.e. for testing.
* You _really_ do _not_ want to turn this off, because if you do,
* the clients send you very small read requests, and a _lot_ more
* of them. The skc_oplock_enable parameter can be used to enable
* or disable exclusive oplocks. Disabling that can be helpful
* when there are clients not responding to oplock breaks.
*/
/*
* smb_oplock_acquire
*
* Attempt to acquire an oplock. Clients will request EXCLUSIVE or BATCH,
* but might only be granted LEVEL_II or NONE.
*
* If oplocks are not supported on the tree, or node, grant NONE.
* If nobody else has the file open, grant the requested level.
* If any of the following are true, grant NONE:
* - there is an exclusive oplock on the node
* - op->op_oplock_levelII is B_FALSE (LEVEL_II not supported by open cmd.
* - LEVEL_II oplocks are not supported for the session
* - a BATCH oplock is requested on a named stream
* - there are any range locks on the node (SMB writers)
* Otherwise, grant LEVEL_II.
*
* ol->ol_xthread is set to the current thread to lock the oplock against
* other operations until the acquire response is on the wire. When the
* acquire response is on the wire, smb_oplock_broadcast() is called to
* reset ol->ol_xthread and wake any waiting threads.
*/
void
{
if (smb_oplocks_enabled == 0 ||
SMB_IS_STREAM(node))) {
return;
}
/*
* Even if there are no other opens, we might want to
* grant only a Level II (shared) oplock so we avoid
* ever granting exclusive oplocks.
*
* exclusive oplocks (for now). See skc_oplock_enable,
* which can now be taken as "exclusive oplock enable".
* multi-valued parameter for oplock enables.
*/
/*
* There are other opens.
*/
if ((!op->op_oplock_levelII) ||
/*
* LevelII (shared) oplock not allowed,
* so reply with "none".
*/
return;
}
}
return;
}
}
/*
* smb_oplock_break
*
* Break granted oplocks according to the following rules:
*
* If there's an exclusive oplock granted on the node
* - if the BREAK_BATCH flags is specified and the oplock is not
* a batch oplock, no break is required.
* - if the session doesn't support LEVEL II oplocks, and 'brk' is
* BREAK_TO_LEVEL_II, do a BREAK_TO_NONE.
* - if the oplock is already breaking update the break level (if
* the requested break is to a lesser level), otherwise send an
* oplock break.
* Wait for acknowledgement of the break (unless NOWAIT flag is set)
*
* Otherwise:
* If there are level II oplocks granted on the node, and the flags
* indicate that they should be broken (BREAK_TO_NONE specified,
* BREAK_EXCLUSIVE, BREAK_BATCH not specified) queue the levelII
* break request for asynchronous processing.
*
* Returns:
* 0 - oplock broken (or no break required)
* EAGAIN - oplock break request sent and would block
* awaiting the reponse but NOWAIT was specified
*
* NB: sr == NULL when called by FEM framework.
*/
int
{
return (0);
}
/* break levelII oplocks */
if ((flags & SMB_OPLOCK_BREAK_TO_NONE) &&
!(flags & SMB_OPLOCK_BREAK_EXCLUSIVE) &&
!(flags & SMB_OPLOCK_BREAK_BATCH)) {
}
return (0);
}
/* break exclusive oplock */
if ((flags & SMB_OPLOCK_BREAK_BATCH) &&
return (0);
}
if ((flags & SMB_OPLOCK_BREAK_TO_LEVEL_II) &&
} else {
}
case SMB_OPLOCK_NO_BREAK:
break;
if (brk == SMB_OPLOCK_BREAK_TO_NONE)
break;
case SMB_OPLOCK_BREAK_TO_NONE:
default:
break;
}
if (flags & SMB_OPLOCK_BREAK_NOWAIT) {
return (EAGAIN);
}
} else {
}
return (0);
}
/*
* smb_oplock_break_levelII
*
* This is called after a file is modified in some way. If there are
* LevelII (shared) oplocks, break those to none. If there is an
* exclusive oplock, there can be no LevelII oplocks, so do nothing.
*
* LevelII (shared) oplock breaks are processed asynchronously.
* Unlike exclusive oplock breaks, the thread initiating the break
* is NOT blocked while the request is processed.
*
* There may be a thread with exclusive rights to oplock state for
* this node (via ol_xthread in smb_oplock_wait) and if so, we must
* avoid breaking oplocks until that's out of the way. However, we
* really don't want to block here, so when ol_xthread is set, we'll
* just mark that a "break level II to none" is pending, and let the
* exclusive thread do this work when it's done being exclusive.
*/
void
{
/* Instead of: smb_oplock_wait() ... */
/* Defer the call to smb_oplock_broadcast(). */
} else {
/* Equivalent of smb_oplock_wait() done. */
}
}
/*
* smb_oplock_break_levelII_locked
* Internal helper for smb_oplock_break_levelII()
*
* Called with the oplock mutex already held, and _after_
* (the equivalent of) an smb_oplock_wait().
*/
static void
{
/*
* If there's an exclusive oplock, there are
* no LevelII oplocks, so do nothing.
*/
break;
}
}
/*
* Schedule a call to smb_session_oplock_break
* using an smb_request on the owning session.
*/
static void
{
/*
* Make sure we can get a hold on the ofile. If we can't,
* the file is closing, and there's no point scheduling an
* oplock break on it. (Also hold the tree and user.)
* These holds account for the pointers we copy into the
* smb_request fields: fid_ofile, tid_tree, uid_user.
* These holds are released via smb_request_free after
* the oplock break has been sent.
*/
if (!smb_ofile_hold(ofile))
return;
(void) taskq_dispatch(
}
/*
* smb_oplock_exec_async_break
*
* Called via the taskq to handle an asynchronous oplock break.
* We have a hold on the ofile, which keeps the FID here valid.
*/
static void
{
case SMB_REQ_STATE_SUBMITTED:
/*
* This is where we actually do the deferred work
* requested by smb_oplock_sched_async_break().
*/
/* FALLTHROUGH */
default: /* typically cancelled */
}
}
/*
* smb_oplock_wait_ack
*
* Timed wait for an oplock break acknowledgement (or oplock release).
*/
static void
{
break;
}
}
}
/*
* smb_oplock_timedout
*
* An oplock break has not been acknowledged within timeout
* 'smb_oplock_timeout'.
* Set oplock grant to the desired break level.
*/
static void
{
if (og) {
case SMB_OPLOCK_BREAK_TO_NONE:
break;
break;
default:
SMB_PANIC();
}
}
}
/*
* smb_oplock_release
*
* Release the oplock granted on ofile 'of'.
* Wake any threads waiting for an oplock break acknowledgement for
* this oplock.
* This is called when the ofile is being closed.
*/
void
{
if (og) {
}
}
}
/*
* smb_oplock_ack
*
* Process oplock acknowledgement received for ofile 'of'.
* - oplock.ol_break is the break level that was requested.
* - brk is the break level being acknowledged by the client.
*
* Update the oplock grant level to the lesser of ol_break and brk.
* If the grant is now SMB_OPLOCK_NONE, remove the grant from the
* oplock's grant list and delete it.
* If the requested break level (ol_break) was NONE and the brk is
* LEVEL_II, send another oplock break (NONE). Do not wait for an
* acknowledgement.
* Wake any threads waiting for the oplock break acknowledgement.
*/
void
{
return;
}
switch (brk) {
case SMB_OPLOCK_BREAK_TO_NONE:
break;
} else {
/* SMB_OPLOCK_BREAK_TO_NONE */
}
break;
default:
SMB_PANIC();
}
}
}
/*
* smb_oplock_broadcast
*
* Called when an open with oplock request completes.
*
* ol->ol_xthread identifies the thread that was performing an oplock
* acquire. Other threads may be blocked awaiting completion of the
* acquire.
* If the calling thread is ol_xthread, wake any waiting threads.
*/
void
{
if (ol->ol_brk_pending) {
ol->ol_brk_pending = 0;
}
}
}
/*
* smb_oplock_wait
*
* Wait for the completion of an oplock acquire.
* If ol_xthread is not NULL and doesn't contain the pointer to the
* context of the calling thread, the caller will sleep until the
* ol_xthread is reset to NULL (via smb_oplock_broadcast()).
*/
static void
{
}
}
/*
* smb_oplock_set_grant
*/
static smb_oplock_grant_t *
{
og->og_breaking = 0;
return (og);
}
/*
* smb_oplock_clear_grant
*/
void
{
}
/*
* smb_oplock_insert_grant
*
* If there are no grants in the oplock's list install the fem
* monitor.
* Insert the grant into the list and increment the grant count.
*/
static int
{
if (smb_oplock_install_fem(node) != 0)
return (-1);
}
return (0);
}
/*
* smb_oplock_remove_grant
*
* Remove the oplock grant from the list, decrement the grant count
* and, if there are no other grants in the list, uninstall the fem
* monitor.
*/
static void
{
}
/*
* smb_oplock_exclusive_grant
*
* If an exclusive (EXCLUSIVE or BATCH) oplock grant exists,
* return it. Otherwise return NULL.
*/
static smb_oplock_grant_t *
{
if (og) {
return (og);
}
return (NULL);
}
/*
* smb_oplock_get_grant
*
* Find oplock grant corresponding to the specified ofile.
*/
static smb_oplock_grant_t *
{
return (&ofile->f_oplock_grant);
else
return (NULL);
}