smb_oplock.c revision 4163af6adeecee26a894ae83a4ffbd3d0f2ec8f2
/*
* 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
*/
/*
*/
/*
* 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>
#define SMB_OPLOCK_IS_EXCLUSIVE(level) \
(((level) == SMB_OPLOCK_EXCLUSIVE) || \
((level) == SMB_OPLOCK_BATCH))
extern int smb_fem_oplock_install(smb_node_t *);
extern int smb_fem_oplock_uninstall(smb_node_t *);
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 smb_oplock_break_t *smb_oplock_get_break(void);
static void smb_oplock_delete_break(smb_oplock_break_t *);
static void smb_oplock_process_levelII_break(smb_node_t *);
static void smb_oplock_break_thread();
/* levelII oplock break requests (smb_oplock_break_t) */
static smb_llist_t smb_oplock_breaks;
static smb_thread_t smb_oplock_thread;
/*
* smb_oplock_init
*
* This function is not multi-thread safe. The caller must make sure only one
* thread makes the call.
*/
int
smb_oplock_init(void)
{
int rc;
return (0);
if (rc != 0) {
return (rc);
}
return (0);
}
/*
* smb_oplock_fini
* This function is not multi-thread safe. The caller must make sure only one
* thread makes the call.
*/
void
smb_oplock_fini(void)
{
if (!smb_oplock_initialized)
return;
}
}
/*
* 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
{
if (smb_fem_oplock_uninstall(node) == 0) {
} else {
"failed to uninstall fem monitor %s",
}
}
}
/*
* 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
* 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
{
SMB_IS_STREAM(node))) {
return;
}
if ((!op->op_oplock_levelII) ||
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
*/
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
*
* LevelII (shared) oplock breaks are processed asynchronously.
* Unlike exclusive oplock breaks, the thread initiating the break
* is NOT blocked while the request is processed.
*
* Create an oplock_break_request and add it to the list for async
* processing.
*/
void
{
}
/*
* smb_oplock_break_thread
*
* The smb_oplock_thread is woken when an oplock break request is
* added to the list of pending levelII oplock break requests.
* Gets the oplock break request from the list, processes it and
* deletes it.
*/
/*ARGSUSED*/
static void
{
while (smb_thread_continue(thread)) {
}
}
}
/*
* smb_oplock_get_break
*
* Remove and return the next oplock break request from the list
*/
static smb_oplock_break_t *
smb_oplock_get_break(void)
{
}
return (ob);
}
/*
* smb_oplock_process_levelII_break
*/
void
{
if (!smb_oplock_levelII)
return;
break;
}
}
/*
* 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();
}
}
if (brk_to_none) {
}
}
/*
* smb_oplock_broadcast
*
* 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_ol_xthread, wake any waiting threads.
*/
void
{
}
}
/*
* 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 *
{
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);
}
/*
* smb_oplock_create_break
*/
static smb_oplock_break_t *
{
return (ob);
}
/*
* smb_oplock_delete_break
*/
static void
{
}