Files
MaxScale/server/core/buffer.c
VilhoRaatikka 86a4c3ba90 Fix to #472, http://bugs.skysql.com/show_bug.cgi?id=472
Fix to #473, http://bugs.skysql.com/show_bug.cgi?id=473

Several memory issues. Read the code.
In general one-off hint is only added to gwbuf and freed in gwbuf_free. Stacked hint is copied to stack and to gwbuf. gwbuf is freed after routing but stacked hint is freed either in stop or when session is closed. All this applies to named hint as well except that in addition, it has one more copy in named hint struct which is emptied when session is closed.
2014-08-08 01:28:07 +03:00

470 lines
11 KiB
C

/*
* This file is distributed as part of the SkySQL Gateway. It is free
* software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation,
* version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright SkySQL Ab 2013
*/
/**
* @file buffer.h - The Gateway buffer management functions
*
* The buffer management is based on the principle of a linked list
* of variable size buffer, the intention beign to allow longer
* content to be buffered in a list and minimise any need to copy
* data between buffers.
*
* @verbatim
* Revision History
*
* Date Who Description
* 10/06/13 Mark Riddoch Initial implementation
* 11/07/13 Mark Riddoch Add reference count mechanism
* 16/07/2013 Massimiliano Pinto Added command type to gwbuf struct
* 24/06/2014 Mark Riddoch Addition of gwbuf_trim
* 15/07/2014 Mark Riddoch Addition of properties
*
* @endverbatim
*/
#include <stdlib.h>
#include <buffer.h>
#include <atomic.h>
#include <skygw_debug.h>
/**
* Allocate a new gateway buffer structure of size bytes.
*
* For now we allocate memory directly from malloc for buffer the management
* structure and the actual data buffer itself. We may swap at a future date
* to a more effecient mechanism.
*
* @param size The size in bytes of the data area required
* @return Pointer to the buffer structure or NULL if memory could not
* be allocated.
*/
GWBUF *
gwbuf_alloc(unsigned int size)
{
GWBUF *rval;
SHARED_BUF *sbuf;
// Allocate the buffer header
if ((rval = (GWBUF *)malloc(sizeof(GWBUF))) == NULL)
{
return NULL;
}
// Allocate the shared data buffer
if ((sbuf = (SHARED_BUF *)malloc(sizeof(SHARED_BUF))) == NULL)
{
free(rval);
return NULL;
}
// Allocate the space for the actual data
if ((sbuf->data = (unsigned char *)malloc(size)) == NULL)
{
free(rval);
free(sbuf);
return NULL;
}
spinlock_init(&rval->lock);
rval->start = sbuf->data;
rval->end = rval->start + size;
sbuf->refcount = 1;
rval->sbuf = sbuf;
rval->next = NULL;
rval->hint = NULL;
rval->properties = NULL;
rval->gwbuf_type = GWBUF_TYPE_UNDEFINED;
rval->command = 0;
CHK_GWBUF(rval);
return rval;
}
/**
* Free a gateway buffer
*
* @param buf The buffer to free
*/
void
gwbuf_free(GWBUF *buf)
{
BUF_PROPERTY *prop;
CHK_GWBUF(buf);
if (atomic_add(&buf->sbuf->refcount, -1) == 1)
{
free(buf->sbuf->data);
free(buf->sbuf);
}
while (buf->properties)
{
prop = buf->properties;
buf->properties = prop->next;
free(prop->name);
free(prop->value);
free(prop);
}
/** Release the hint */
while (buf->hint)
{
HINT* h = buf->hint;
buf->hint = buf->hint->next;
hint_free(h);
}
free(buf);
}
/**
* Increment the usage count of a gateway buffer. This gets a new
* GWBUF structure that shares the actual data with the existing
* GWBUF structure but allows for the data copy to be avoided and
* also for each GWBUF to point to different portions of the same
* SHARED_BUF.
*
* @param buf The buffer to use
* @return A new GWBUF structure
*/
GWBUF *
gwbuf_clone(GWBUF *buf)
{
GWBUF *rval;
if ((rval = (GWBUF *)malloc(sizeof(GWBUF))) == NULL)
{
return NULL;
}
atomic_add(&buf->sbuf->refcount, 1);
rval->sbuf = buf->sbuf;
rval->start = buf->start;
rval->end = buf->end;
rval->gwbuf_type = buf->gwbuf_type;
rval->properties = NULL;
rval->hint = NULL;
rval->next = NULL;
CHK_GWBUF(rval);
return rval;
}
GWBUF *gwbuf_clone_portion(
GWBUF *buf,
size_t start_offset,
size_t length)
{
GWBUF* clonebuf;
CHK_GWBUF(buf);
ss_dassert(start_offset+length <= GWBUF_LENGTH(buf));
if ((clonebuf = (GWBUF *)malloc(sizeof(GWBUF))) == NULL)
{
return NULL;
}
atomic_add(&buf->sbuf->refcount, 1);
clonebuf->sbuf = buf->sbuf;
clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone info bits too */
clonebuf->start = (void *)((char*)buf->start)+start_offset;
clonebuf->end = (void *)((char *)clonebuf->start)+length;
clonebuf->gwbuf_type = buf->gwbuf_type; /*< clone the type for now */
clonebuf->properties = NULL;
clonebuf->hint = NULL;
clonebuf->next = NULL;
CHK_GWBUF(clonebuf);
return clonebuf;
}
/**
* Returns pointer to GWBUF of a requested type.
* As of 10.3.14 only MySQL to plain text conversion is supported.
* Return NULL if conversion between types is not supported or due lacking
* type information.
*/
GWBUF *gwbuf_clone_transform(
GWBUF * head,
gwbuf_type_t targettype)
{
gwbuf_type_t src_type;
GWBUF* clonebuf;
CHK_GWBUF(head);
src_type = head->gwbuf_type;
if (targettype == GWBUF_TYPE_UNDEFINED ||
src_type == GWBUF_TYPE_UNDEFINED ||
src_type == GWBUF_TYPE_PLAINSQL ||
targettype == src_type)
{
clonebuf = NULL;
goto return_clonebuf;
}
if (GWBUF_IS_TYPE_MYSQL(head))
{
if (GWBUF_TYPE_PLAINSQL == targettype)
{
/** Crete reference to string part of buffer */
clonebuf = gwbuf_clone_portion(
head,
5,
GWBUF_LENGTH(head)-5);
ss_dassert(clonebuf != NULL);
/** Overwrite the type with new format */
gwbuf_set_type(clonebuf, targettype);
}
else
{
clonebuf = NULL;
}
}
else
{
clonebuf = NULL;
}
return_clonebuf:
return clonebuf;
}
/**
* Append a buffer onto a linked list of buffer structures.
*
* This call should be made with the caller holding the lock for the linked
* list.
*
* @param head The current head of the linked list
* @param tail The new buffer to make the tail of the linked list
* @return The new head of the linked list
*/
GWBUF *
gwbuf_append(GWBUF *head, GWBUF *tail)
{
GWBUF *ptr = head;
if (!head)
return tail;
CHK_GWBUF(head);
while (ptr->next)
{
ptr = ptr->next;
}
ptr->next = tail;
return head;
}
/**
* Consume data from a buffer in the linked list. The assumption is to consume
* n bytes from the buffer chain.
*
* If after consuming the bytes from the first buffer that buffer becomes
* empty it will be freed and the linked list updated.
*
* The return value is the new head of the linked list.
*
* This call should be made with the caller holding the lock for the linked
* list.
*
* @param head The head of the linked list
* @param length The amount of data to consume
* @return The head of the linked list
*/
GWBUF *
gwbuf_consume(GWBUF *head, unsigned int length)
{
GWBUF *rval = head;
CHK_GWBUF(head);
GWBUF_CONSUME(head, length);
CHK_GWBUF(head);
if (GWBUF_EMPTY(head))
{
rval = head->next;
gwbuf_free(head);
}
return rval;
}
/**
* Return the number of bytes of data in the linked list.
*
* @param head The current head of the linked list
* @return The number of bytes of data in the linked list
*/
unsigned int
gwbuf_length(GWBUF *head)
{
int rval = 0;
if (head)
{
CHK_GWBUF(head);
}
while (head)
{
rval += GWBUF_LENGTH(head);
head = head->next;
}
return rval;
}
/**
* Trim bytes form the end of a GWBUF structure
*
* @param buf The buffer to trim
* @param nbytes The number of bytes to trim off
* @return The buffer chain
*/
GWBUF *
gwbuf_trim(GWBUF *buf, unsigned int n_bytes)
{
if (GWBUF_LENGTH(buf) <= n_bytes)
{
gwbuf_consume(buf, GWBUF_LENGTH(buf));
return NULL;
}
buf->end -= n_bytes;
return buf;
}
/**
* Set given type to all buffers on the list.
* *
* @param buf The shared buffer
* @param type Type to be added
*/
void gwbuf_set_type(
GWBUF* buf,
gwbuf_type_t type)
{
/** Set type consistenly to all buffers on the list */
while (buf != NULL)
{
CHK_GWBUF(buf);
buf->gwbuf_type |= type;
buf=buf->next;
}
}
/**
* Add a property to a buffer.
*
* @param buf The buffer to add the property to
* @param name The property name
* @param value The property value
* @return Non-zero on success
*/
int
gwbuf_add_property(GWBUF *buf, char *name, char *value)
{
BUF_PROPERTY *prop;
if ((prop = malloc(sizeof(BUF_PROPERTY))) == NULL)
return 0;
prop->name = strdup(name);
prop->value = strdup(value);
spinlock_acquire(&buf->lock);
prop->next = buf->properties;
buf->properties = prop;
spinlock_release(&buf->lock);
return 1;
}
/**
* Return the value of a buffer property
* @param buf The buffer itself
* @param name The name of the property to return
* @return The property value or NULL if the property was not found.
*/
char *
gwbuf_get_property(GWBUF *buf, char *name)
{
BUF_PROPERTY *prop;
spinlock_acquire(&buf->lock);
prop = buf->properties;
while (prop && strcmp(prop->name, name) != 0)
prop = prop->next;
spinlock_release(&buf->lock);
if (prop)
return prop->value;
return NULL;
}
/**
* Convert a chain of GWBUF structures into a single GWBUF structure
*
* @param orig The chain to convert
* @return The contiguous buffer
*/
GWBUF *
gwbuf_make_contiguous(GWBUF *orig)
{
GWBUF *newbuf;
char *ptr;
int len;
if (orig->next == NULL)
return orig;
if ((newbuf = gwbuf_alloc(gwbuf_length(orig))) != NULL)
{
ptr = GWBUF_DATA(newbuf);
while (orig)
{
len = GWBUF_LENGTH(orig);
memcpy(ptr, GWBUF_DATA(orig), len);
ptr += len;
orig = gwbuf_consume(orig, len);
}
}
return newbuf;
}
/**
* Add hint to a buffer.
*
* @param buf The buffer to add the hint to
* @param hint The hint itself
* @return Non-zero on success
*/
int
gwbuf_add_hint(GWBUF *buf, HINT *hint)
{
HINT *ptr;
spinlock_acquire(&buf->lock);
if (buf->hint)
{
ptr = buf->hint;
while (ptr->next)
ptr = ptr->next;
ptr->next = hint;
}
else
{
buf->hint = hint;
}
spinlock_release(&buf->lock);
return 1;
}