The printcap update procedure involves the background printer process
obtaining the printcap information from the printing backend, writing
this to printer_list.tdb, and then notifying all smbd processes of the
new list. The processes then all attempt to simultaneously traverse
printer_list.tdb, in order to update their local share lists.
With a large number of printers, and a large number of per-client smbd
processes, this traversal results in significant lock contention, mostly
due to the fact that the traversal is unnecessarily done with an
exclusive (write) lock on the printer_list.tdb database.
This commit changes the share update code path to perform a read-only
traversal.
Bug: https://bugzilla.samba.org/show_bug.cgi?id=10652
Reported-by: Alex K <korobkin+samba@gmail.com>
Reported-by: Franz Pförtsch <franz.pfoertsch@brose.com>
Signed-off-by: David Disseldorp <ddiss@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
(cherry picked from commit
1e83435eac2cef03fccb4cf69ef5e0bfbd710410)
/* load all printcap printers */
if (lp_load_printers() && lp_servicenumber(PRINTERS_NAME) >= 0)
- pcap_printer_fn(lp_add_one_printer, NULL);
+ pcap_printer_read_fn(lp_add_one_printer, NULL);
}
return;
}
-void pcap_printer_fn(void (*fn)(const char *, const char *, const char *, void *), void *pdata)
+void pcap_printer_read_fn(void (*fn)(const char *, const char *, const char *, void *), void *pdata)
{
NTSTATUS status;
- status = printer_list_run_fn(fn, pdata);
+ status = printer_list_read_run_fn(fn, pdata);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(3, ("Failed to run fn for all printers!\n"));
}
bool pcap_cache_loaded(void);
bool pcap_cache_replace(const struct pcap_cache *cache);
void pcap_printer_fn_specific(const struct pcap_cache *, void (*fn)(const char *, const char *, const char *, void *), void *);
-void pcap_printer_fn(void (*fn)(const char *, const char *, const char *, void *), void *);
+void pcap_printer_read_fn(void (*fn)(const char *, const char *, const char *, void *), void *);
void pcap_cache_reload(struct tevent_context *ev,
struct messaging_context *msg_ctx,
typedef int (printer_list_trv_fn_t)(struct db_record *, void *);
static NTSTATUS printer_list_traverse(printer_list_trv_fn_t *fn,
- void *private_data)
+ void *private_data,
+ bool read_only)
{
struct db_context *db;
NTSTATUS status;
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
- status = dbwrap_traverse(db, fn, private_data, NULL);
+ if (read_only) {
+ status = dbwrap_traverse_read(db, fn, private_data, NULL);
+ } else {
+ status = dbwrap_traverse(db, fn, private_data, NULL);
+ }
return status;
}
state.status = NT_STATUS_OK;
- status = printer_list_traverse(printer_list_clean_fn, &state);
+ status = printer_list_traverse(printer_list_clean_fn, &state, false);
if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) &&
!NT_STATUS_IS_OK(state.status)) {
status = state.status;
return 0;
}
-NTSTATUS printer_list_run_fn(void (*fn)(const char *, const char *, const char *, void *),
- void *private_data)
+NTSTATUS printer_list_read_run_fn(void (*fn)(const char *, const char *, const char *, void *),
+ void *private_data)
{
struct printer_list_exec_state state;
NTSTATUS status;
state.private_data = private_data;
state.status = NT_STATUS_OK;
- status = printer_list_traverse(printer_list_exec_fn, &state);
+ status = printer_list_traverse(printer_list_exec_fn, &state, true);
if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) &&
!NT_STATUS_IS_OK(state.status)) {
status = state.status;
*/
NTSTATUS printer_list_clean_old(void);
-NTSTATUS printer_list_run_fn(void (*fn)(const char *, const char *, const char *, void *),
- void *private_data);
+NTSTATUS printer_list_read_run_fn(void (*fn)(const char *, const char *, const char *, void *),
+ void *private_data);
#endif /* _PRINTER_LIST_H_ */